Codeforces 2220
天上天下、唯我独尊。
天上天下、唯我独尊
現状原点、結ガ独奏。
现状原点、结作独奏
Codeforces Round 1093 (Div. 2)
A.Blocked

直接降序就行,注意不能有重复元素
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=1e5+5;
int a[N];
bool vis[N];
inline void solve(){
cin>>n;
for(int i=1;i<=100;i++){
vis[i]=false;
}
bool flag=false;
for(int i=1;i<=n;i++){
cin>>a[i];
if(vis[a[i]]){
flag=true;
}
else{
vis[a[i]]=true;
}
}
if(flag){
cout<<"-1\n";
return;
}
sort(a+1,a+n+1,greater<int>());
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
cout<<'\n';
return;
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
B. OIE Excursion

如果存在一个长度 \(≥ m\) 的连续等值子数组,则不可能逃脱。这是因为穿越这样的区块至少需要 \(m\) 秒,而每个位置在任何连续的 \(m\) 秒内至少会向 \(0\) 方向移动一次。
否则,逃逸是可能的。我们可以把数组看成连续的等值区块,每个区块的长度都小于 \(m\)。对于每个区块来说,都存在一个小于 \(m\) 的时间窗口,在这个窗口内,没有任何一个位置会看向 \(0\)。由于两个连续的区块具有不同的值,因此它们的危险时刻永远不会重合。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,x;
const int N=2e5+5;
int a;
inline void solve(){
cin>>n>>m;
int c,cnt,mx=1;
for(int i=1;i<=n;i++){
cin>>a;
if(i==1){
c=a,cnt=1;
}
else if(a!=c){
c=a;
mx=max(mx,cnt);
cnt=1;
}
else{
cnt++;
}
}
mx=max(mx,cnt);
if(mx<m) {
cout<<"YES\n";
}
else{
cout<<"NO\n";
}
return;
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
C. Grid L

假设最后是 \(n\times m\) 的网格,则有 \(n\times (m+1)\) 个竖线, \(m \times (n+1)\) 个横线,总共 \(n+m+2nm\) 个线,易得:
所以化成两个奇数相乘得到 \(tot=2p+4q+1\),于是我们可以枚举 \(tot\) 的奇数因数,得到另一个数,如果也是奇数的话说明满足这个式子。
然后还要满足第二个条件,就是 \(L\) 型可以分成一个横线一个竖线,然后一型的可以补齐,所以需要竖线数和横线数不能超过 \(q\),即:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,p,q;
const int N=1e5+5;
int a[N];
inline void solve(){
cin>>p>>q;
int tot=p+q*2;
tot*=2;
tot++;
for(int i=3;i<=sqrt(tot);i+=2){
if(tot%i==0){
int ano=tot/i;
if(ano&1){
int tmp1=(i-1)/2,tmp2=(ano-1)/2;
if(tmp1*(tmp2+1)<q||(tmp1+1)*tmp2<q){
continue;
}
cout<<tmp1<<" "<<tmp2<<'\n';
return;
}
}
else{
continue;
}
}
cout<<"-1\n";
return;
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
D2. Unique Values (Hard version)

对于任意查询的集合,记 \(S_1\) 是只出现了一次的元素, \(S_2\) 是出现了两次的元素, \(S_3\) 是出现了三次的元素。设返回值是\(J\)
将两边都模 \(2\):
- 当 \(S_3=0\) 时:
- 当 \(S_3=1\) 时:
也就是说我们选取的集合中:
- 如果包含三个相同元素,那么集合大小 \(k\) 和查询返回值就不相等
- 如果不包含三个相同元素,那么集合大小 \(k\) 和查询返回值就相等
所以使用二分先从区间 \([1,n]\) 中找到区间 \([1,r_1]\) 包含三个元素。
然后第二次二分就从区间 \([1,r_1-1]\cup r_1\) 中筛出区间 \([1,r_2]\cup r_1\) 包含三个元素。
最后第三次二分就从区间 $[1,r_2-1]\cup r_1 \cup $ r_2中筛出区间 \([1,r_3]\cup r_1 \cup r_2\) 包含三个元素。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=1e5+5;
inline int query1(int k){
cout<<"? "<<k<<" ";
for(int i=1;i<=k;i++){
cout<<i<<" ";
}
cout<<endl;
int res;
cin>>res;
return res;
}
inline int query2(int k,int Ans1){
cout<<"? "<<k+1<<" ";
for(int i=1;i<=k;i++){
cout<<i<<" ";
}
cout<<Ans1<<endl;
int res;
cin>>res;
return res;
}
inline int query3(int k,int Ans1,int Ans2){
cout<<"? "<<k+2<<" ";
for(int i=1;i<=k;i++){
cout<<i<<" ";
}
cout<<Ans1<<" "<<Ans2<<endl;
int res;
cin>>res;
return res;
}
inline void solve(){
cin>>n;
int l=1,r=2*n+1,ans1=0;
while(l<=r){
int mid=(l+r)>>1;
int res=query1(mid);
if((res&1)==(mid&1)){
l=mid+1;
}
else{
ans1=mid;
r=mid-1;
}
}
l=1,r=ans1-1;
int ans2=0;
while(l<=r){
int mid=(l+r)>>1;
int res=query2(mid,ans1);
if((res&1)==((mid+1)&1)){
l=mid+1;
}
else{
ans2=mid;
r=mid-1;
}
}
l=1,r=ans2-1;
int ans3=0;
while(l<=r){
int mid=(l+r)>>1;
int res=query3(mid,ans1,ans2);
if((res&1)==((mid+2)&1)){
l=mid+1;
}
else{
ans3=mid;
r=mid-1;
}
}
cout<<"! "<<ans1<<" "<<ans2<<" "<<ans3<<endl;
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
要注意第二分的大小要加上 \(1\),第三次二分的大小要加上 \(2\)。

浙公网安备 33010602011771号