• Codeforces Global Round 13


    Codeforces Global Round 13

    前言

    吐了,本来是上分局的。。
    这么简单一套题我都能打成这个傻逼样子。。。

    A 略

    B Minimal Cost

    题意

    给一个n行(1e6+2)列的图,用(i,j)表示i行j列上的点,(1leq i leq n,0leq jleq 1e6+1),然后第i行的(a_i)是障碍,不能通过。现在可以移动障碍,水平移动一格花费v,上下移动一格花费u,要使(1,0)能到达(n,1e6+1),求最小花费。(1leq n leq 100,1leq a_i leq {10}^{6})

    题解

    做法很简单,但我当时太蠢了没想到。这题过了我应该就不至于掉分了。
    首先因为(1leq a_ileq 1e6),所以j=0和j=1e6+1两列都是无障碍的。
    我们要做的就是能从第0列到第1e6+1列。
    这样就少了很多特殊情况了,显然只需要让某相邻两行的a的差的绝对值大于1或者让某一行空出来。
    于是分类讨论一下就行了。

    (Code)

    #include <bits/stdc++.h>
    #define LL long long
    #define LD long double
    using namespace std;
    const LL P=998244353;
    const int N=3e5+10;
    const int INF=1e9;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void print(LL x){
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    void pls(LL &x,LL y){
        x+=y;if(x>=P)x-=P;
    }
    int n;
    int a[110];
    void MAIN(){
        int u,v;
        n=read();u=read();v=read();
        for(int i=1;i<=n;++i){
            a[i]=read();
        }
        int ans=min(u,v)+v;
        for(int i=2;i<=n;++i){
            if(abs(a[i]-a[i-1])>1) ans=min(ans,0);
        }
        for(int i=2;i<=n;++i){
            if(abs(a[i]-a[i-1])==1) ans=min(ans,min(u,v));
        }
        printf("%d
    ",ans);
        return;
    }
    
    int main(){
        int ttt=read();
        while(ttt--) MAIN();
        return 0;
    }
    

    C 略

    D 略

    E Fib-tree

    题意

    如果一棵树点数是斐波那契数列的某一项,并且满足点数为1或者能分成两个斐波那契树,则称这棵树是斐波那契树。现在给定一棵树,判断是否是斐波那契树。(1leq n leq 200000)

    题解

    首先如果树点数是(Fib_{k+2}),那么被分出的两颗树的点数一定分别是(Fib_{k+1},Fib_k)
    然后会发现一棵(Fib_{k+2})树,可能有0-2条能把树分成(Fib_{k+1},Fib_{k})的边。
    如果有0条,那肯定是不行了。
    如果有1条,那只能把这条边拆掉了。
    如果有两条,那么不管先拆哪条,最终结果都是一样的(这个看起来挺明显的,不清楚怎么证)。
    然后一直暴力拆就可以了。因为Fib数列长度是log级别的,暴力拆分效率也是(O(nlogn))的。

    (Code)

    #include <bits/stdc++.h>
    #define LL long long
    #define LD long double
    using namespace std;
    const LL P=998244353;
    const int N=3e5+10;
    const int INF=1e9;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void print(LL x){
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    void pls(LL &x,LL y){
        x+=y;if(x>=P)x-=P;
    }
    int n,cnt;
    int fib[N],mp[N];
    int hed[N];
    int vis[N],used[N];
    struct edge{
        int r,nxt,id;
    }e[N<<1];
    void insert(int u,int v,int w){
        e[++cnt].r=v;e[cnt].nxt=hed[u];hed[u]=cnt;e[cnt].id=w;
    }
    int fa[N],sz[N];
    void dfs1(int x,int t){
        vis[x]=t;sz[x]=1;
        for(int i=hed[x];i;i=e[i].nxt){
            if(used[e[i].id]) continue;
            if(e[i].r==fa[x]) continue;
            fa[e[i].r]=x;
            dfs1(e[i].r,t);
            sz[x]+=sz[e[i].r];
        }
    }
    int X;
    int tp;
    int q[N];
    void dfs2(int x){
        for(int i=hed[x];i;i=e[i].nxt){
            if(used[e[i].id]) continue;
            if(e[i].r==fa[x]) continue;
            dfs2(e[i].r);
            if(mp[sz[e[i].r]]==X-1||mp[sz[e[i].r]]==X-2){
                q[++tp]=e[i].id;
            }
        }
    }
    void MAIN(){
        n=read();
        fib[1]=1;fib[2]=2;
        mp[1]=1;mp[2]=2;
        for(int i=3;;++i){
            fib[i]=fib[i-1]+fib[i-2];
            if(fib[i]>n) break;
            mp[fib[i]]=i;
        }
        int u,v;
        for(int i=1;i<n;++i){
            u=read();v=read();
            insert(u,v,i);
            insert(v,u,i);
        }
        int mx=n,t=0;
        int flag=0;
        while(mx>3){
            ++t;mx=0;
            for(int i=1;i<=n;++i){
                if(vis[i]!=t){
                    fa[i]=0;
                    dfs1(i,t);
                    mx=max(mx,sz[i]);
                    if(mp[sz[i]]==0){
                        flag=1;break;
                    }
                    if(sz[i]<=3) continue;
                    tp=0;X=mp[sz[i]];
                    dfs2(i);
                    if(!tp){
                        flag=1;break;
                    }
                    while(tp>0){
                        used[q[tp]]=1;
                        --tp;
                    }
                }
            }
            if(flag==1) break;
        }
        if(flag) puts("NO");
        else puts("YES");
        return;
    }
     
    int main(){
        int ttt=1;
        while(ttt--) MAIN();
        return 0;
    }
    

    F Magnets

    题意

    n个磁铁,分为3类,S,N,-(没有磁性),现在要从中找到所有没有磁性的。可以进行(n+lfloor log_2n floor)次询问,有一个仪器,每次询问可以在仪器右边放若干个磁铁,左边放若干个磁铁,仪器可以测出两边磁铁产生的磁力((S_lS_r+N_lN_r-S_lN_r-N_lS_r)),数据保证至少有1个没有磁性和2个有磁性的。仪器的测量上限是n,也就是说测出的值不能超过n,否则仪器会崩溃。(1leq n leq 2000)

    题解

    这题思路不难,但出题人是真的抠门。询问次数刚好卡满,一次都没多。
    我当时已经想到了,但是比要求的多询问了两次。。本来能上大分的(委屈屈QAQ)
    首先肯定要找到一个有磁性的,这样其他的磁铁可以直接通过询问这个磁铁来判断。
    要找磁铁,肯定要使里能测出来。
    从左到右枚举,询问第i个磁铁跟后面所有的磁铁,这样测出的值显然不会超过量程,出现测量值不为0就找到了。
    这样我们就在不到n次内找到了一个磁铁,设为K。
    此时我们已知K和(K+1,K+2,...,n)的答案,然后依次询问K和(K+2,K+3,..,n)....(n)。
    这样加上之前找磁铁的次数,一共n-1次。并且我们已经能判断K到n所有磁铁是否有磁性。
    然后考虑之前的怎么弄,前面的询问答案肯定是0,这些询问没有有用信息。
    但是有一个性质:前面最多有一个有磁性的(在询问那个磁铁的时候,右边SN一样多)。
    这样就可以二分了,二分的次数是(lceil log_2n ceil)。加上n-1次次数刚好够。

    (Code)

    #include <bits/stdc++.h>
    #define LL long long
    #define LD long double
    using namespace std;
    const LL P=998244353;
    const int N=3e5+10;
    const int INF=1e9;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void print(LL x){
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    void pls(LL &x,LL y){
        x+=y;if(x>=P)x-=P;
    }
    
    int n,res,K,T,tp;
    int q[N];
    int b[N];
    void MAIN(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            printf("? %d %d
    ",1,n-i);
            fflush(stdout);
            printf("%d
    ",i);
            fflush(stdout);
            for(int j=i+1;j<=n;++j){
                printf("%d ",j);
            }
            printf("
    ");
            fflush(stdout);
            scanf("%d",&res);
            if(res!=0){
                K=i;
                b[K+1]=res;
                break;
            }
        }
        for(int i=K+2;i<=n;++i){
            printf("? %d %d
    ",1,n-i+1);
            fflush(stdout);
            printf("%d
    ",K);
            fflush(stdout);
            for(int j=i;j<=n;++j){
                printf("%d ",j);
            }
            printf("
    ");
            fflush(stdout);
            scanf("%d",&res);
            b[i]=res;
        }
        
        tp=0;b[n+1]=0;
        for(int i=n;i>K;--i){
            if(b[i]==b[i+1]) q[++tp]=i;
        }
        T=0;
        if(K>1){
            n=K-1;
            int l=1,r=n,mid,fff=0;
            while(l<r){
                    mid=(l+r)>>1;
                    printf("? %d %d
    ",1,mid-l+1);
                    fflush(stdout);
                    printf("%d
    ",K);
                    fflush(stdout);
                    for(int j=l;j<=mid;++j){
                        printf("%d ",j);
                    }
                    printf("
    ");
                    fflush(stdout);
                    scanf("%d",&res);
                    if(res==0) l=mid+1;
                    else {
                        r=mid;
                        fff=1;
                    }
            }
            T=l;
            if(fff==0){
                printf("? %d %d
    ",1,1);
                fflush(stdout);
                printf("%d
    ",K);
                fflush(stdout);
                printf("%d
    ",T);
                fflush(stdout);
                scanf("%d",&res);
                fff=res;
            }
            if(fff==0) {
                for(int j=1;j<=n;++j) q[++tp]=j;
            }
            else {
                for(int j=1;j<=n;++j) if(j!=T) q[++tp]=j;
            }
        }
        printf("! %d",tp);
        for(int i=1;i<=tp;++i) printf(" %d",q[i]);
        printf("
    ");
        fflush(stdout);
        return;
    }
    
    int main(){
        int ttt;scanf("%d",&ttt);
        while(ttt--) MAIN();
        return 0;
    }
    

    G Switch and Flip

    题意

    有一个长度为n排列(c_i),最多可以进行n+1次操作,使得(c_i=i)。每次操作选择两个不同的数i,j((1leq i,j leq n)),交换(c_i)(c_j)的值,并且(c_i)(c_j)都乘上-1。(1leq n leq 200000)

    题解

    比赛的时候没做到这题,现在看来这比F还水啊QAQ。
    一看题目要求O(n)级别的次数来换完,容易想到之歌跟置换有关。
    于是先把所有循环给预处理出来。
    然后分两种情况讨论。
    第1种情况,只有1个循环长度为n的循环。我们设其为(2,3,4,...,n,1)。
    然后有一种比较显然的换法。
    (2,3,4,5,...,n,1)->(2,-1,4,5,...,n,-3)->(2,-1,3,5,...,n,-4)->...->(2,-1,3,4,...,n-1,-n)->(n,-1,3,4,...,n-1,-2)->(1,-n,3,4,...,n-1,-2)->(1,2,3,.....,n)
    第2种情况,有多个循环。这种情况我们只需考虑如何合并两个循环。设为(a2,a3,a4,..,an,a1)(b2,b3,b4,..,bn,b1)
    合并方法。
    (a2,a3,a4,..,an,a1)(b2,b3,b4,..,bn,b1)->(-b2,a3,a4,..,an,a1)(-a2,b3,b4,..,bn,b1)->...->(-b1,a3,a4,..,an,a1)(-a2,b2,b3,..,bn)->..->(-b1,a2,a3,..,an)(-a1,b2,b3,..,bn)->(a1,a2,a3,..,an)(b1,b2,b3,..,bn)
    如果循环个数是偶数个,两两合并即可。如果是奇数个,那么最后多出一个,最后随便取一个循环长度为1的循环来做即可。

    (Code)

    #include <bits/stdc++.h>
    #define LL long long
    #define LD long double
    using namespace std;
    const LL P=998244353;
    const int N=3e5+10;
    const int INF=1e9;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void print(LL x){
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    void pls(LL &x,LL y){
        x+=y;if(x>=P)x-=P;
    }
    int n,cnt;
    int a[N];
    int len[N];
    bool vis[N];
    vector<int> p[N];
    int t;
    int l[N],r[N];
    void MAIN(){
        n=read();cnt=0;
        for(int i=1;i<=n;++i) {
            a[i]=read();
            vis[i]=0;
        }
        //for(int i=1;i<=n;++i) cout<<a[i]<<" ";puts("");
        for(int i=1;i<=n;++i){
            if(!vis[i]){
                ++cnt;
                p[cnt].clear();
                len[cnt]=0;
                for(int j=i;;j=a[j]){
                    if(!vis[j]){
                        vis[j]=1;
                        ++len[cnt];
                        p[cnt].push_back(j);
                    }
                    else break;
                }
            }
        }
        t=0;
        if(cnt==1){
            for(int i=1;i<len[cnt]-1;++i){
                ++t;
                l[t]=p[cnt][i];r[t]=p[cnt][len[cnt]-1];
            }
            ++t;
            l[t]=p[cnt][0];r[t]=p[cnt][len[cnt]-1];
            ++t;
            l[t]=p[cnt][0];r[t]=p[cnt][1];
            ++t;
            l[t]=p[cnt][1];r[t]=p[cnt][len[cnt]-1];
        }
        else{
            while(cnt>=2){
                ++t;
                l[t]=p[cnt][0];r[t]=p[cnt-1][0];
                for(int i=1;i<len[cnt];++i){
                    ++t;
                    l[t]=p[cnt][i];r[t]=p[cnt-1][0];
                }
                for(int i=1;i<len[cnt-1];++i){
                    ++t;
                    l[t]=p[cnt-1][i];r[t]=p[cnt][0];
                }
                ++t;
                l[t]=p[cnt][0];r[t]=p[cnt-1][0];
                cnt-=2;
            }
            if(cnt==1){
                ++cnt;
                len[cnt]=1;
                ++t;
                l[t]=p[cnt][0];r[t]=p[cnt-1][0];
                for(int i=1;i<len[cnt];++i){
                    ++t;
                    l[t]=p[cnt][i];r[t]=p[cnt-1][0];
                }
                for(int i=1;i<len[cnt-1];++i){
                    ++t;
                    l[t]=p[cnt-1][i];r[t]=p[cnt][0];
                }
                ++t;
                l[t]=p[cnt][0];r[t]=p[cnt-1][0];
                cnt-=2;
            }
        }
        printf("%d
    ",t);
        for(int i=1;i<=t;++i){
            printf("%d %d
    ",l[i],r[i]);
        }
        return;
    }
    
    int main(){
        int ttt=1;
        while(ttt--) MAIN();
        return 0;
    }
    

    H Yuezheng Ling and Dynamic Tree

    题意

    给定一个数组(a_i(2leq i leq n)),表示树中节点的父亲。现在要维护两种操作,一共Q次。1.给定(l,r,x),令([l,r])区间内的(a_i=max(1,a_i-x))。2.给定(u,v),求两点在树中的最近公共祖先。(1leq n,Q leq 100000)

    题解

    这东西一看就不能建树。。。
    再仔细一想就很容易发现这跟弹飞绵羊那道题几乎一样。。。
    先分块,维护一下块内的点通过边能到达的编号最小的块内的点,设为(b_i)。。
    因为(a_i)只会不断变小,而且当(a_i)小于这个块的左端点之后,这个块内会满足(b_i=i)
    这样子的话,修改的方式就很明朗了。
    两边的块直接暴力,中间的块看一下是不是所有点都满足(b_i=i),如果是,打个标记就行,如果不是就暴力。
    两边的块的效率肯定是根号的,打标记肯能保证根号,块内暴力的部分每个点最多减根号次(超过根号次肯定(b_i=i)了)。
    询问也很直接,假设询问的点是(u,v(u<v))
    如果两个点不在同一个块内,靠后的点(v)一定跳到(fa(b_v))
    否则在同一块内,如果(b_u=b_v),说明lca就在这个块内,接下来暴力跳是根号的。
    再然后如果(b_ub_v)不相等,两个点都跳出这个块。
    每次询问都最多跨越根号个块,并且只会在某个块内暴力跳,都是根号的。
    效率就是(O((n+Q)sqrt(n)))

    (Code)

    #include <bits/stdc++.h>
    #define LL long long
    #define LD long double
    using namespace std;
    const LL P=998244353;
    const int N=3e5+10;
    const int INF=1e9;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void print(LL x){
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    void pls(LL &x,LL y){
        x+=y;if(x>=P)x-=P;
    }
    const int E=320;
    int n,Q;
    int a[N],g[N],tag[N],mx[N],b[N];
    void init(int i){
        int L=(i-1)*E+1,R=min(n,i*E);
        mx[i]=0;
        for(int j=L;j<=R;++j){
            if(a[j]>mx[i]) mx[i]=a[j];
            if(a[j]<L) b[j]=j;
            else b[j]=b[a[j]];
        }
    }
    void MAIN(){
        int L,R;
        n=read();Q=read();
        for(int i=2;i<=n;++i) a[i]=read();
        a[1]=1;
        for(int i=1;i<=n;++i) g[i]=(i+E-1)/E;
        for(int i=1;i<=g[n];++i) {
            tag[i]=0;
            init(i);
        }
        int op,l,r,x;
        while(Q--){
            op=read();l=read();r=read();
            if(op==1){
                x=read();
                if(g[l]==g[r]){
                    for(int i=l;i<=r;++i){
                        a[i]=max(a[i]-x,1);
                    }
                    if(mx[g[l]]>(g[l]-1)*E) init(g[l]);
                }
                else{
                    for(int i=l;i<=g[l]*E;++i){
                        a[i]=max(a[i]-x,1);
                    }
                    if(mx[g[l]]>(g[l]-1)*E) init(g[l]);
                    
                    for(int i=(g[r]-1)*E+1;i<=r;++i){
                        a[i]=max(a[i]-x,1);
                    }
                    if(mx[g[r]]>(g[r]-1)*E) init(g[r]);
                    
                    for(int i=g[l]+1;i<g[r];++i){
                        if(mx[i]<(i-1)*E) tag[i]+=x;
                        else{
                            for(int j=(i-1)*E+1;j<=i*E;++j){
                                a[j]=max(a[j]-x,1);
                            }
                            init(i);
                        }
                    }
                }
            }
            else{
                while(1){
                    if(l==r) break;
                    if(l>r) swap(l,r);
                    if(g[l]!=g[r]){
                        r=max(a[b[r]]-tag[g[r]],1);
                        continue;
                    }
                    if(b[l]!=b[r]){
                        l=max(a[b[l]]-tag[g[l]],1);
                        r=max(a[b[r]]-tag[g[r]],1);
                        continue;
                    }
                    r=max(a[r]-tag[g[r]],1);
                }
                printf("%d
    ",l);
            }
        }
        return;
    }
    
    int main(){
        int ttt=1;
        while(ttt--) MAIN();
        return 0;
    }
    

    I Ruler Of The Zoo

    过于毒瘤,不补这题了QAQ

  • 相关阅读:
    openstack Rocky系列之Cinder:(一)Cinder服务启动
    openstack Rocky系列之keystone:(二)keystone中API注册
    openstack Rocky系列之keystone:(一)keystone的启动
    Ubuntu16.04搭建kubernetes v1.11.2集群
    HIVE创建分区,添加数据
    深度优先和广度优先
    创建appium容器以及设置appium容器连接安卓模拟器
    appium+fiddler——douyin小爬虫
    命令_查看端口占用情况 netstat -ano|findstr PID
    aapt 命令查看apk包名、主activity、版本等信息
  • 原文地址:https://www.cnblogs.com/Yuigahama/p/14463349.html
Copyright © 2020-2023  润新知