Codeforces Round #812 (Div. 2)A-E
Codeforces Round #812 (Div. 2)A-E
过程
这场A卡了一下,最大最小值初值应该都为0,之后B,C一遍过,而D中间一个地方x写成n了,白交二次罚时,最后E想到了二分图的性质,但只是用了vis标记,赛后学习了种类并查集已补,另外二分图染色应该也是可做的。
传送门
题目
A
注意到有\(x_i,y_i\)总有一个为0,那么答案即为\(x_{max}+y_{max}-x_{min}-y_{min}\)
void solve(){
int n;cin>>n;
int a,b,c,d;
a=b=c=d=0;
rep(i,1,n){
int x,y;
cin>>x>>y;
if(x<=0) a=min(x,a);
if(x>=0)c=max(c,x);
if(y<=0)b=min(y,b);
if(y>=0)d=max(d,y);
}
cout<<(-a-b+c+d)*2<<endl;
}
B
显然只有递增,递减,先递增再递减这3种可能,判断即可。
int n;
int a[maxn];
bool check(){
rep(i,2,n){
if(a[i]<a[i-1]) return 0;
}
return 1;
}
bool solve(){
cin>>n;
rep(i,1,n) cin>>a[i];
if(check()) return 1;
reverse(a+1,a+1+n);
if(check()) return 1;
int pos;
rep(i,2,n){
if(a[i]<a[i-1]) {
pos=i;break;
}
}
rep(i,pos,n-1){
if(a[i]<a[i+1]) return 0;
}
return 1;
}
C
挺有意思的题,首先是没有-1的情况,对于任意的排列\(0...(n-1)\),设\(a^2\leq(n-1)\leq(a+1)^2\),那么因为\((a+1)^2 - a^2=2*a+1\leq a^2若a\geq3\),故当\(a\geq3\)区间\([2*a+1,n-1]\)内的数总可以相互配对来凑成完全平方数,重复这个过程可完成此题。
int n;
vector<int>sq;
bool vis[maxn];
void solve(){
cin>>n;
rep(i,0,n) vis[i]=0;
vector<int>ans;
int now=lower_bound(sq.begin(),sq.end(),n-1)-sq.begin();
//sq为预处理出的平方数表
rpe(i,n-1,0){
int tmp=sq[now]-i;
if(!vis[tmp] && tmp<=n-1){
vis[tmp]=1;
ans.pb(tmp);
}
else{
while(vis[tmp]||tmp>n-1){
now--;
tmp=sq[now]-i;
}
vis[tmp]=1;
ans.pb(tmp);
}
}
reverse(ans.begin(),ans.end());
for(auto x:ans) cout<<x<<" ";
pts;
}
D
交互题首先需要想到二分,不过这道题不是。注意如果每场都要问需要\(\sum_{i=0}^{n-1}2^i=2^n-1\)次询问,那么我们需要想办法减少询问次数,对\(n=2\)时思考,可以发现,4个人的比赛只需要进行二次询问即可得知胜出者。四人中胜场数分别为2,1,0,0,那么我们首先询问(1,3),如果二者胜场相等,那么询问(2,4)中胜场多的即为胜出者;如果1胜场多于3,那么将1与4进行比较,如果1胜场多于4,说明1为胜出者,否则4胜场一定比1多,则4为胜出者;当3胜场多时同1。因此查询数量减少为\(\sum_{i=0}^{n/2} 4^i\approx \frac{2^n}{3}\),可通过此题。
int n;
vector<int>a,b;
int ask(int x,int y){
printf("? %d %d\n",x,y);
fflush(stdout);
int z;cin>>z;
fflush(stdout);
return z;
}
void dfs(int x){
if(x==1) {
printf("! %d\n",a[1]);
fflush(stdout);
return;
}
if(x==2){
int z=ask(a[1],a[2]);
if(z==1) z=a[1];
else z=a[2];
printf("! %d\n",z);
fflush(stdout);
return;
}
b.clear();b.pb(0);
for(int i=1;i<=x;i+=4){
int tep=ask(a[i],a[i+2]);
if(tep==0){
tep=ask(a[i+1],a[i+3]);
if(tep==1) b.pb(a[i+1]);
else b.pb(a[i+3]);
}
else if(tep==1){
tep=ask(a[i],a[i+3]);
if(tep==2) b.pb(a[i+3]);
else b.pb(a[i]);
}
else{
tep=ask(a[i+1],a[i+2]);
if(tep==2) b.pb(a[i+2]);
else b.pb(a[i+1]);
}
}
a=b;
dfs(x/4);
}
void solve(){
cin>>n;n=(1<<n);
a.clear();b.clear();
a.resize(n+1);b.resize(n+1);
rep(i,1,n) a[i]=i;
dfs(n);
}
E
因为要字典序最小,因此首先满足较为前面字符的需求,即优先满足先出现的\(a[i][j]<a[j][i]\),否则进行交换,需要注意的是第\(i\)行和第\(j\)列的交换即可以由\(k=i\),也可以有\(k=j\)来完成,因此对于\(a[i][j]>a[j][i],k=i,k=j\)不同时进行交换,而\(a[i][j]<a[j][i],k=i,k=j\)需要同时或均不做进行交换,这里相当于一个二分图,第一种情况两个点必须不同颜色,第二种情况必须相同颜色。
一般的并查集可以维护亲戚的亲戚是亲戚,此处可以进行拓展,使得其满足敌人的敌人是朋友,朋友的朋友依旧是朋友,这就是种类并查集,挂一个知乎。
我们用种类并查集来维持这个关系,对于第一种情况,在二者不为朋友关系时我们合并\((i,j+n),(i+n,j)\)表明\(i,j\)为敌对关系,二者只能存一,对于第二种情况,在二者不为敌对关系时,合并\((i,j)(i+n,j+n)\),表明二者为朋友关系。最后会形成若干个连通块,每个连通块中,所有小于n的即为朋友关系,其中大于n的即为朋友们对应的敌人。最后会形成两个大的连通块,且连个连通块的根节点一个大于n,一个小于n,且两个连通块的内容互补,选一个连通块执行里面小于等于n的操作数执行。
struct DSU{
vector<int>fa;
void build(int n){
fa.clear();fa.resize(n+1);
rep(i,1,n) fa[i]=i;
}
int find(int x){return x==fa[x]? x:fa[x]=find(fa[x]);}
void merge(int x,int y){fa[find(x)]=find(y);}
int same(int x,int y){return find(x)==find(y);}
}dsu;
void solve(){
cin>>n;
dsu.build(n<<1);
rep(i,1,n){
rep(j,1,n) scanf("%d",&a[i][j]);
}
rep(i,1,n){
rep(j,1,n){
if(a[i][j]>a[j][i]){
if(!dsu.same(i,j)){
dsu.merge(i,j+n);
dsu.merge(i+n,j);
}
}
else if(a[i][j]<a[j][i]){
if(!dsu.same(i,j+n)){
dsu.merge(i,j);
dsu.merge(i+n,j+n);
}
}
}
}
rep(k,1,n){
if(dsu.find(k)>n){
rep(i,1,n) swap(a[i][k],a[k][i]);
}
}
rep(i,1,n){
rep(j,1,n) printf("%d ",a[i][j]);
pts;
}
}