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;
    }
}  
posted @ 2022-08-07 22:35  Mr_cold  阅读(60)  评论(0编辑  收藏  举报