zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 026题解

    前四个题都很基础啊。

    E - Synchronized Subsequence

    这种思路不是很好想啊。

    我们发现这个题直接逐位确定非常的难啊,根本没法做。

    正解是将原序列分成了若干个段,使得$a$的个数等于$b$的个数。

    并且要保证是最小的划分。

    之后有一个结论就是在同一段里$a$与对应$b$的前后关系是一样的。

    这个可以用不存在一种更小的划分来证,即中间不存在前缀$cnt[a]==cnt[b]$。

    对于$a$在$b$i前面的段,显然分成$ababab......$最优。

    对于$a$在$b$后面的段,是一个完整的后缀最优。

    证明的话就是我们删掉一个前缀并选择剩下的一些点后,剩下没有选的位置都是$b$,所以一定是选上最优。

    第二种我用了一个$cnt$代表开头$b$的个数还有一个串$S$来表示这个字符串。

    这样就可以用后缀自动机$O(1)Lcp$来优化到$O(n)$了。

    #include <bits/stdc++.h>
    #define min(a,b) ((a)<(b)?(a):(b))
    #define max(a,b) ((a)>(b)?(a):(b))
    #define for1(a,b,i) for(int i=a;i<=b;++i)
    #define FOR2(a,b,i) for(int i=a;i>=b;--i)
    using namespace std;
    typedef long long ll;
    inline int read() {
        int f=1,sum=0;
        char x=getchar();
        for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1;
        for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0';
        return f*sum;
    }
    
    #define N 10000
    #define M 1000005
    int n;
    char s[M];
    int be[M],f[M],buc[M];
    int id[M],Max[M],pos[M],nxt[M],sum[M],L[M],R[M];
    
    inline bool check(int x,int xr,int y,int yr) {
        int len=min(xr-x,yr-y);
        for1(0,len,i)
            if(s[x+i]!=s[y+i]) return s[x+i]<s[y+i];
        return xr-x<=yr-y;
    }
    
    inline bool check_Max(int x,int y) {
        if(f[x]<N&&f[y]>N) return 0;
        if(f[x]>N&&f[y]<N) return 1;
        if(f[x]<N) return f[x]>=f[y];
        if(f[x]/N>f[y]/N||(f[x]/N==f[y]/N&&check(f[y]%N,R[y],f[x]%N,R[x]))) return 1;
        return 0;
    }
    
    int main() {
        //freopen("a.in","r",stdin);
        //freopen("mine.out","w",stdout);
        n=read()*2;
        scanf("%s",s+1);
        int shu=0;
        for1(1,n,i) {
            if(!shu) be[i]=++be[0];
            else     be[i]=be[i-1];
            shu+=('a'==s[i])-('b'==s[i]);
        }
        for1(1,n,i) R[be[i]]=i;
        FOR2(n,1,i) L[be[i]]=i;
        
        for(int l=1,r;l<=n;l=r+1) {
            r=l;
            while (be[r+1]==be[l]) ++r;
            if(s[l]=='a') {
                //怎么都没想到这里错啊,太马虎了
                int h=1,t=0;
                for1(l,r,i) 
                    if(s[i]=='a') buc[++t]=i;
                    else pos[buc[h++]]=i;
                int now=l;
                while (1) {
                    ++f[be[l]];
                    now=pos[now]+1;
                    while (now<=r&&s[now]!='a') ++now;
                    if(now>r) break;
                }
            }
            else {
                sum[l-1]=0;
                for1(l,r,i) sum[i]=sum[i-1]+(s[i]=='b');
                int h=1,t=0;
                for1(l,r,i) 
                    if(s[i]=='b') buc[++t]=i;
                    else pos[buc[h++]]=i;
                FOR2(r,l,i)
                    if(s[i]=='b') nxt[i]=nxt[i+1];
                    else nxt[i]=i;
                int pre=sum[pos[l]],id=pos[l];
                for1(l,r-1,i) if(s[i]=='b') {
                    int x[2]={sum[pos[i]]-sum[i],pos[i]+1};
                    x[0]+=nxt[x[1]]-x[1];
                    x[1]=nxt[x[1]];
                    if(x[0]>pre||(x[0]==pre&&check(id,r,x[1],r))) pre=x[0],id=x[1];
                }
                f[be[l]]=pre*N+id;
            }
        }
        
        //abab是累加,不能比较字典序。。
        FOR2(be[0],1,i) {
            Max[i]=Max[i+1];
            if(f[i]>N&&check_Max(i,Max[i])) Max[i]=i;
        }
        //for1(1,be[0],i) cout<<f[be[i]]<<" "; cout<<endl;
        for1(1,be[0],i) if(!Max[i]) Max[i]=i;
        int pos=0;
        while (1) {
            pos=Max[pos+1];
            if(f[pos]>N) {
                FOR2(f[pos]/N,1,i) putchar('b');
                for1(f[pos]%N,R[pos],i) putchar(s[i]);
            }
            else {
                for1(1,f[pos],i) putchar('a'),putchar('b');
            }
            if(pos==be[0]) break;
        }
        puts("");
    }

    F - Manju Game

    $O(n^3)$是显然的,然后就想不出来了。

    正解分析性质就很多了。。

    首先黑白染色,$10101010......$。

    若$n$是偶数,有两个结论:

    1>先手答案大于等于$max(B,W)$

    2>后手答案大于等于$min(B,W)$

    第一个好证啊。。直接选一个边界就好了,第二个直接归纳法证一下就好了。

    这样$n$为偶数就可以直接输出了。

    当$n$为奇数时,第一步若我们选了黑色,依然好说。

    但是选白色就比较复杂了。

    我们考虑因为如果我们选白色,之后一定一直都是我们先手,这样我们就得到了一个白色点的集合。

    显然我们可以在某个区间内让我们选黑色,后手选白色,其他区间我们要白色,后手选黑色。

    如果二分答案之后存在一个白色点的集合使得任意一个区间都满足$B-W>=mid$即可。

    这个显然是正确的。因为我们选的那个区间一定是$B-W$最小的区间,如果后手不让我们选这个区间是更亏的。

    #include <bits/stdc++.h>
    #define for1(a,b,i) for(int i=a;i<=b;++i)
    #define FOR2(a,b,i) for(int i=a;i>=b;--i)
    using namespace std;
    
    #define M 1000005
    int n;
    int a[M];
    
    inline bool check(int x) {
        int sum=0;
        for(int i=1;i<=n;i+=2) {
            if(sum>=x) sum=max(sum+a[i]-a[i-1],a[i]);
            else sum+=a[i]-a[i-1];
        }
        return sum>=x;
    }
    
    int main () {
        //freopen("a.in","r",stdin);
        scanf("%d",&n);
        for1(1,n,i) scanf("%d",a+i);
        int sum[2]={0};
        for1(1,n,i) sum[i&1]+=a[i];
        if(!(n&1)) printf("%d %d
    ",max(sum[0],sum[1]),min(sum[0],sum[1]));
        else {
            int l=0,r=sum[1],mid,ans;
            while (l<=r) {
                mid=l+r>>1;
                if(check(mid)) ans=mid,l=mid+1;
                else r=mid-1;
            }
            printf("%d %d
    ",sum[0]+ans,sum[1]-ans);
        }
    }
  • 相关阅读:
    POJ 2017 No Brainer(超级水题)
    POJ 2017 No Brainer(超级水题)
    Poj1207 The 3n + 1 problem(水题(数据)+陷阱)
    Poj1207 The 3n + 1 problem(水题(数据)+陷阱)
    Poj1159 Palindrome(动态规划DP求最大公共子序列LCS)
    Poj1159 Palindrome(动态规划DP求最大公共子序列LCS)
    Poj1050_To the Max(二维数组最大字段和)
    Poj1050_To the Max(二维数组最大字段和)
    Poj 2602 Superlong sums(大数相加)
    Poj 2602 Superlong sums(大数相加)
  • 原文地址:https://www.cnblogs.com/asd123www/p/9712789.html
Copyright © 2011-2022 走看看