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;
}
posted @ 2021-11-13 11:13  EncodeTalker  阅读(132)  评论(0编辑  收藏  举报