• Codeforces Round #754 (Div. 2) 部分题解


    A

    考虑差值\(|a_1+a_3-2a_2|\),每次操作对其的影响为\(\pm 3\),所以只需要判断差值是否是\(3\)的倍数。

    int main()
    {
        int T=read();
        while (T--)
        {
            int a=read(),b=read(),c=read();
            int x=abs(a+c-b-b);
            if (x%3) puts("1");else puts("0");
        }
        return 0;
    }
    

    B

    操作的关键是将前面的1和后面的0进行交换,贪心的将这些操作合并即可构造出一种最小方案。

    int n,cnt0,b[N],m;
    vi ans[N];
    char s[N];
    
    bool chk()
    {
        rep(i,1,cnt0) if (s[i]!='0') return 0;
        return 1;
    }
    
    int main()
    {
        int T=read();
        while (T--)
        {
            n=read();cnt0=0;
            scanf("%s",s+1);
            rep(i,1,n) cnt0+=(s[i]=='0');
            m=0;
            while (!chk())
            {
                m++;int len=0;
                int l=1,r=n;
                while (l<r)
                {
                    while ((l<=n) && (s[l]=='0')) l++;
                    while ((r>=1) && (s[r]=='1')) r--;
                    if (l>=r) break;
                    b[++len]=l;b[++len]=r;
                    l++;r--;
                }
                for (int i=1;i<=len;i+=2) swap(s[b[i]],s[b[i+1]]);
                for (int i=1;i<=len;i+=2) ans[m].pb(b[i]);
                for (int i=len;i>=1;i-=2) ans[m].pb(b[i]);
            }
            printf("%d\n",m);
            rep(i,1,m)
            {
                int len=ans[i].size();
                printf("%d ",len);
                rep(j,0,len-1) printf("%d ",ans[i][j]);puts("");
                ans[i].clear();
            }
        }
        return 0;
    }
    

    C

    通过手玩可以发现:最小的合法串不会很长(最长的串为abbaccaaccabba),所以直接枚举所有长度\(\leq 7\)的子串即可。

    int cnt[N];
        
    int main()
    {
        int T=read();
        while (T--)
        {
            string s;int n;cin >> n >> s;
            int ans=n+1;
            rep(i,0,n-1)
            {
                cnt['a']=cnt['b']=cnt['c']=0;
                cnt[s[i]]++;
                rep(j,1,7)
                {
                    if (i+j==n) break;
                    cnt[s[i+j]]++;
                    if ((cnt['a']>cnt['b']) && (cnt['a']>cnt['c']))
                    {
                        ans=min(ans,j+1);
                        break;
                    }
                }
            }
            if (ans>n) ans=-1;
            printf("%d\n",ans);
        }
        return 0;
    }
    

    D

    根据Div2D不会很难知道这个题肯定会有简明结论

    给出结论:存在一种构造方案使得所有边都会断开,从而先手无论选哪个点都能获得胜利。接下来通过给出构造方案来证明这一点。

    我们的构造方案要满足\(\forall (u,v)\in E,u \ \mathrm{xor} \ v>\min(u,v)\),不妨设\(u>v\),考虑\(u\)的二进制的最高位,不难发现原条件等价于在这一位上\(v\)只能为\(0\)。故\(\forall (u,v)\in E\),我们要求\(u,v\)两数在二进制下的位数不同。

    接下来考虑这个方案能否实现,将\(1\sim n\)拆分成\([2^0,2^1),[2^1,2^2),\cdots,[2^{p-1},2^p),[2^p,n]\)\(p+1\)个部分,从而不同部分的数在而今之下的位数不同,且除最后一部分外第\(i\)部分有\(2^{i-1}\)个点。

    由于树是一张二分图,我们可以将其黑白染色。对点数较少(\(\leq \frac{n}{2}\))的那半边点,我们可以将其二进制拆分,使得它能被上面的前\(p\)个部分的一个子集覆盖掉。我们再将剩下的部分分给另半边的点即可。

    struct node{int to,nxt;}sq[N<<1];
    int all=0,head[N];
    int n,ans[N],p0[N],p1[N],r0,r1,x[N];
    
    void addedge(int u,int v)
    {
        all++;sq[all].to=v;sq[all].nxt=head[u];head[u]=all;
    }
    
    void dfs(int u,int fu,int c)
    {
        if (c) p1[++r1]=u;else p0[++r0]=u;
        go(u,i)
        {
            if (v==fu) continue;
            dfs(v,u,c^1);
        }
    }
    
    int main()
    {
        int T=read();
        while (T--)
        {
            n=read();
            rep(i,1,n-1)
            {
                int u=read(),v=read();
                addedge(u,v);addedge(v,u);
            }
            r0=0;r1=0;
            dfs(1,0,0);
            int p=0;
            while (1)
            {
                x[p+1]=(1<<p);p++;
                if (x[p]>n) {x[p]=n+1;break;}
            }
            per(i,p,1)
            {
                int l=x[i-1],r=x[i]-1,len=r-l+1;
                if (r0>=len)
                {
                    rep(j,l,r) 
                    {
                        ans[p0[r0]]=j;r0--;
                    }
                }
                else 
                {
                    rep(j,l,r)
                    {
                        ans[p1[r1]]=j;r1--;
                    }
                }
            }
            rep(i,1,n) printf("%d ",ans[i]);puts("");
            all=0;
            rep(i,1,n) head[i]=0;
        }
        return 0;
    }
    

    E

    所有的\(a_i\to 0,b_i\to b_i-a_i\)的问题和原问题等价。

    先考虑对于给定的\(b_1\)怎么做。不难想到一个贪心做法:从1开始向后遍历,由于当前位置的数只能被当前位置的操作影响到,所以在这个位置的操作可以计算得到。根据调和级数知暴力做操作的复杂度是\(O(n\log n)\)的。

    考虑\(b_1\)发生变动时的情况:假设当前的\(b_1\)\(x\),那么照搬上面的做法可以得到第\(i\)次的操作是一个一次函数\(c_ix+d_i\)。最后的答案就是令\(x=x_0\)后计算\(\sum |c_ix_0+d_i|\)。可以先预处理出所有的一次函数,去除所有的常函数后将剩下的按零点从小到大排序。那么对于一次询问的答案必然是前半边取相反数而后半边不懂,这个分界点可以直接二分得到。

    struct Poly{
        ll a,b;
        Poly(ll _a=0,ll _b=0) {a=_a;b=_b;}
    };
    Poly operator +(Poly a,Poly b) 
    {
        return Poly(a.a+b.a,a.b+b.b);
    }
    
    Poly operator -(Poly a,Poly b)
    {
        return Poly(a.a-b.a,a.b-b.b);
    }
    int n,a[N],b[N],m;
    Poly p[N],nowp[N],pre[N],suf[N],pp[N];
    bool cmp(Poly x,Poly y) {return x.b*y.a<x.a*y.b;}
    
    int main()
    {
        n=read();
        rep(i,1,n) a[i]=read();
        rep(i,1,n) b[i]=read();
        rep(i,2,n) b[i]-=a[i];
        rep(i,1,n) {p[i].a=1;p[i].b=0;nowp[i].a=1;nowp[i].b=0;}
        rep(i,2,n)
        {
            p[i]=Poly(0,b[i])-nowp[i];
            for (int j=i;j<=n;j+=i) nowp[j]=nowp[j]+p[i];
        }
        ll c=0;
        
        rep(i,1,n)
        {
            if (p[i].a==0) c+=abs(p[i].b);
            else 
            {
                pp[++m]=p[i];
                if (pp[m].a<0) {pp[m].a*=-1;pp[m].b*=-1;}
            }
        }
        sort(pp+1,pp+1+m,cmp);
        rep(i,1,m) pre[i]=pre[i-1]+pp[i];
        per(i,m,1) suf[i]=suf[i+1]+pp[i];
        int q=read();
        while (q--)
        {
            int x=read(),l=1,r=m,pos=0;x-=a[1];
            while (l<=r)
            {
                int mid=(l+r)>>1;
                if (pp[mid].a*x+pp[mid].b<=0) {pos=mid;l=mid+1;}
                else r=mid-1;
            }
            ll ans1=pre[pos].a*x+pre[pos].b,
               ans2=suf[pos+1].a*x+suf[pos+1].b;
            printf("%lld\n",ans2-ans1+c);
        }
        return 0;
    }
    
  • 相关阅读:
    java String 转Json报错 java.lang.NoClassDefFoundError: org/apache/commons/lang/exception/NestableRuntim
    Idea 进行断点调试的 快捷键
    spring AOP的使用步骤
    AOP的定义和原理
    SpringBoot入门篇--使用IDEA创建一个SpringBoot项目
    详解CI、CD相关概念
    intellij idea 的全局搜索快捷键方法
    面向对象之工厂模式与构造函数模式的区别
    冒泡排序和快速排序
    java里的数组和list分别在什么情况下使用?
  • 原文地址:https://www.cnblogs.com/encodetalker/p/15547534.html
Copyright © 2020-2023  润新知