• Codeforces Round#402(Div.1)掉分记+题解


    哎,今天第一次打div1 感觉头脑很不清醒。。。

    看到第一题就蒙了,想了好久,怎么乱dp,倒过来插之类的...突然发现不就是一道sb二分吗.....sb二分看了二十分钟........

    然后第二题看了一下,感觉太码农了不可做,然后就cd逛一逛。

    突然觉得c可做,就做了一下,交上去wa了,发现有情况没考虑。

    这下又滚回了b,结果又没写完......

    gg  2040 -83 ->1957

    -----------------------------------------------我似分割线啊

    A.String Game

    题意:有一个n个字符组成的字符串,并给定它的一个子串。调皮的小孩一个人会按照时间顺序每一秒删掉其中一个字符,求一个最迟的时间,使得给定的串仍然是剩下的字符串的子串。n<=200000

    题解:别想太多,直接二分答案,On 来check一下。

    复杂度nlogn

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define ll long longh
    #define INF 2000000000
    #define MAXN 200000
    using namespace std;
    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;
    } 
    
    char s2[MAXN+5];
    char s[MAXN+5];
    int n,m;
    int a[MAXN+5];
    int p[MAXN+5];
    
    bool check(int x)
    {
        int j=1;
        for(int i=1;i<=n&&j<=m;i++)
            if(p[i]>x&&s[i]==s2[j]) ++j;
        if(j>m) return true;
        return false;
    }
    
    int main()
    {
        scanf("%s",s+1);
        scanf("%s",s2+1);
        n=strlen(s+1);m=strlen(s2+1);
        for(int i=1;i<=n;i++)
        {
            a[i]=read();
            p[a[i]]=i;
        }
        int l=0,r=n,mid,ans;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(check(mid)) ans=mid,l=mid+1;
            else r=mid-1;    
        }
        cout<<ans;
        return 0;
    } 

    B.Bitwise Formula

    题意:给定n个变量的定义,每个变量都是m位的二进制数,你可以选择一个数,这些变量在定义的时候可能会用到之前的变量和你选择的数,最后的贡献为所有的变量大小之和。你要分别在贡献最大和最小的情况下,选择最小的数字。

    n<=5000 m<=1000

    题解:贪心。对每一位分别check(),枚举那一位选择1或者0,算一下这一位上的贡献,确定取值即可。复杂度nm

    我的代码丑

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<map>
    #define ID 20170226
    #define ll long long
    using namespace std;
    inline 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;
    }
    string st,st2,name;
    map<string,int> mp;
    char s[10000];
    int mark[5005][3];
    int x[5005][1005],x2[5005][1005];
    int n,m,ans[5005];
    char op[5005];
    int f[5005];
    
    int check(int j,int def)
    {
        int x1,y1,tot=0;
        for(int i=1;i<=n;i++)
        {
            if(mark[i][1]==ID) x1=x[i][j];
            else if(mark[i][1]==-1) x1=def;
            else x1=f[mark[i][1]];
            if(mark[i][2]==ID)y1=x2[i][j];
            else if(mark[i][2]==-1) y1=def;
            else y1=f[mark[i][2]];
           // cout<<i<<" "<<j<<" "<<x1<<" "<<y1<<endl;
            if(op[i]=='O') x1=x1|y1;
            if(op[i]=='A') x1=x1&y1;
            if(op[i]=='X') x1=x1^y1;
            f[i]=x1;tot+=x1;
        }
        return tot;
    }
    
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++)
        {
            int nown=0;
            cin>>name;mp[name]=i;
            scanf("%s",s);cin>>st;
            if(getchar()!='
    ')
            {
                scanf("%s",s);
                cin>>st2;
                if(st2=="?") mark[i][2]=-1;
                else if(st2[0]>='0'&&st2[0]<='9')
                { mark[i][2]=ID;for(int j=0;j<m;j++) x2[i][j]=st2[j]-'0';}
                else mark[i][2]=mp[st2];
                op[i]=s[0];
            }
            else mark[i][2]=ID,op[i]='O';
            if(st=="?") mark[i][1]=-1;
            else if(st[0]>='0'&&st[0]<='9')
            { mark[i][1]=ID;for(int j=0;j<m;j++) x[i][j]=st[j]-'0';}
            else mark[i][1]=mp[st];
    
        }
        for(int i=0;i<m;i++)
        {
            int x=check(i,0);int y=check(i,1);
            if(x>y) printf("1");
            else if(x==y) printf("0");
            else printf("0"),ans[i]=1;
        }
        puts("");
        for(int i=0;i<m;i++)printf("%d",ans[i]);
        return 0;
    }

    C.给定一棵trie树,你可以删掉其中一个深度的点,求重建的trie树最少有多少个点以及最少时删掉哪一个深度。

    题解:如图:

     我们可以发现,删掉一个深度之后减少的点的数量不仅仅是这个深度的节点的数量,还包括这个 深度的爸爸相同(1)的点 (2,3) 的相同字母(c) 的边指向的点(5,4)

    这个例子中4和5可以合成一个点,实际上减少的部分还包括了4和5的相同字母的边指向的点,以此类推。

    所以我们可以考虑在dfs的时候直接暴力算每个点的可合并孙子的个数,并且加入对应深度的答案当中,然后递归下去做。

    具体实现见代码(向cf的一些dalao学习的)

    复杂度是ditoly帮我证明的,这样做的复杂度,因为是两两合并,应该会严格小于启发式合并的复杂度,所以复杂度大概是26*n*logn

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<map>
    #define MAXN 600000
    using namespace std;
    inline 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;
    }
    
    
    int n,m;
    int c[MAXN+5][26];
    char ch;
    int sum[MAXN+5];
    
    int newnode(int x)
    {
        for(int i=0;i<26;i++) c[m][i]=c[x][i];
        return m++;
    }
    
    void merge(int&x,int y,int dep)
    {
        if(!x||!y){x+=y;return;}
        x=newnode(x);++sum[dep];
        for(int i=0;i<26;i++)merge(c[x][i],c[y][i],dep);
    }
    
    void dfs(int x,int dep)
    {
        sum[dep-1]++;int nown=0;m=n+1;
        for(int i=0;i<26;i++,nown=0)
            for(int j=0;j<26;j++)
                if(c[c[x][j]][i])
                    merge(nown,c[c[x][j]][i],dep);
        for(int i=0;i<26;i++) if(c[x][i]) dfs(c[x][i],dep+1);
    }
    
    int main()
    {
        n=read();
        for(int i=1;i<n;i++)
        {
            int u=read(),v=read();scanf("%c",&ch);
            c[u][ch-'a']=v;
        }
        dfs(1,1);int ans=1;
        for(int i=1;i<=n;i++) if(sum[i]>sum[ans]) ans=i;
        printf("%d
    %d",n-sum[ans],ans);
        return 0;
    }

    D.Parquet Re-laying

    有两个n*m并由1*2的小矩形构成的图,每次可以把两个构成2*2的小矩形的块旋转一下(横的变成竖的,竖的变成横的)

    要从第一个图变到第二个图,求一种方案。n,m<=50

    很显然,不管图是什么样,都能转到全是横的 或者全是竖的 的情况,所以这道题其实没有无解...

    所以把两个图都转成全是横的或者全是竖的情况,倒着输出第二个就没了.....

    #include<iostream>
    #include<cstdio>
    using namespace std;
    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;
    }
    
    char s[55][55],s2[55][55],ch;
    int n,m,cnt=0;
    struct ANS{
        int x,y;
    }A[100005];
    
    bool check(char a[55][55])
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                if(a[i][j]==ch) return 0;
        return 1;
    }
    
    void solve(char a[55][55])
    {
        while(1)
        {
            bool ok=1;
            for(;ok;)
            {
                ok=0;
                for(int i=1;i<n;i++)
                    for(int j=1;j<m;j++)
                        if(a[i][j]=='L'&&a[i+1][j]=='L')
                        {
                            ok=1;A[++cnt]=(ANS){i,j};
                            a[i][j]='U';a[i+1][j]='D';
                            a[i][j+1]='U';a[i+1][j+1]='D';
                        }    
            }    
            if(check(a)) break;
            for(ok=1;ok;)
            {
                ok=0;
                for(int i=1;i<n;i++)
                    for(int j=1;j<m;j++)
                        if(a[i][j]=='U'&&a[i][j+1]=='U')
                        {
                            ok=1;A[++cnt]=(ANS){i,j};
                            a[i][j]='L';a[i+1][j]='L';
                            a[i][j+1]='R';a[i+1][j+1]='R';
                        }        
            }
            if(check(a)) break;
        }
    }
    
    int main()
    {
        n=read();m=read();ch=n&1?'U':'L';
        for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
        for(int i=1;i<=n;i++)scanf("%s",s2[i]+1);
        solve(s);int pre=cnt;
        solve(s2);
        cout<<cnt<<endl;
        for(int i=1;i<=pre;i++) printf("%d %d
    ",A[i].x,A[i].y);
        for(int i=cnt;i>pre;i--)    printf("%d %d
    ",A[i].x,A[i].y);
        return 0;
    }

     E.Selling Numbers

    做题背景:今天(第二天,周一)下午和ditoly大佬切完了前四题之后准备看一看第五题,看了一下,发现好像比cd都可做?好像是个数位dp,但是不知道怎么做。

    然后就去standing里面,发现只有30个左右的人过了,就开始一个个翻代码(没几个能看的),能看也看的懵逼,就得出了一个结论:它们都在排序。

    然后就懵懵地去上体育课,想着想着,发现排序后进位连续,这样就可以表示状态了,于是就没了。

    题目大意:给定n个最多1000位的十进制数,你可以选择一个数(有些位数是确定的),并把剩余的数都加上它,求贡献最大值。

    贡献计算:每个数字都有一定的贡献值,每个数的贡献是它的所有数字的贡献值之和。

    n<=1000

    题解:很显然我们能够得到几个结论:

    每一位分开计算->如果不考虑进位就是一道贪心->进位的处理是题目难点

    怎么处理进位呢?我们发现 对于一个数的任意两位,假设是ab 和cd

    如果a>c 那么a>=c+1,如果第二个数进位了,第一个也会进位

    如果a==c,那么b>=d时也有同样的结论

    所以我们可以把每一位都分别以这一位的数为第一关键字,后缀为第二关键字做一遍计数排序,

    这样就能保证进位的部分肯定是连续的一段。

    那么我们就可以用数位dp来计算结果了。

    用f[i][j]表示后i位数字,第i位的前j个有进位的时候的最大答案。

    先枚举数位,再枚举这一位选的数字,按照上一位的大小顺序,让它们一个个进位,同步更新答案。

    f[i][j]=max(f[i-1][k]+合并的答案)并且第i位的数字加上上一位的k个进位,再加上这一位的选择的数字有j个进位

    复杂度 10*n^2 (10*1000*1000)

    然后代码丑

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define INF 2000000000
    using namespace std;
    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;
    }
    int a[15];
    int n,m,len2;
    int len[1005];
    int b[1005];
    char s[1005],s2[1005];
    int x[1005][1005];
    int f[1005][1005];
    int sa[1005][1005];
    int rk[1005][1005];
    int v[1002];
    void sort()
    {
        for(int i=1;i<=n;i++) rk[0][i]=INF;
        for(int i=1;i<=n;i++) sa[0][i]=i;
        for(int i=1;i<=m;i++)
        {
            memset(v,0,sizeof(v));
            for(int j=1;j<=n;j++) v[x[j][i]]++;
            for(int j=9;j>=0;j--) v[j]+=v[j+1];
            for(int j=n;j;j--) rk[i][sa[i-1][j]]=v[x[sa[i-1][j]][i]]--;
            for(int j=1;j<=n;j++) sa[i][rk[i][j]]=j;
        }
    }
    
    int work(int k,int ad,int&tot)
    {
        int en=n,fn=0,i;
        for(register int ii=1;ii<=n;ii++)
        {
            i=sa[k][ii];b[i]=0;
            int xx=x[i][k]+ad;
            if(en==n&&xx<10) en=ii-1;
            if(k>len2&&len[i]<k&&xx==0) continue;
            fn+=a[xx%10];tot+=(xx>=10);b[i]=xx;
        }
        if(f[k-1][0]>=0) f[k][en]=max(f[k][en],f[k-1][0]+fn);
        return fn;
    }
    
    void ins(int i,int j,int ad,int&num,int&into)
    {
        int xx=x[j][i]+ad;
        num=num-a[xx%10]+a[(xx+1)%10];
        if(i>len2&&i>len[j]&&b[j]==0)
            num+=a[xx%10];
        into+=(xx==9);
    }
    
    int main()
    {
        m=1000;
        scanf("%s",s+1);len2=strlen(s+1);
        for(int i=len2,j=1;i;i--,j++) s2[j]=s[i];
        for(int i=len2+1;i<=m;i++) s2[i]='0';
        n=read();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s+1);len[i]=strlen(s+1);
            for(int j=len[i],k=1;j;j--,k++)
                x[i][k]=s[j]-'0';
        }
        for(int i=0;i<10;i++) a[i]=read();
        sort();
        for(int i=0;i<=m;i++)
            for(int j=0;j<=n;j++) f[i][j]=-INF;
        f[0][0]=0;
        for(register int i=1;i<=m;i++)
        {
            if(s2[i]=='?')
            {
                for(register int th=0;th<10;th++)
                {
                    if(th==0&&i==len2) continue;
                    int into=0;
                    int num=work(i,th,into);
                    for(register int j=1;j<=n;j++)
                    {
                        ins(i,sa[i-1][j],th,num,into);
                        if(f[i-1][j]>=0) 
                        {
                            f[i][into]=max(f[i][into],f[i-1][j]+num);
                        }
                    }
                }
            }
            else 
            {
                int th=s2[i]-'0';
                int into=0;
                int num=work(i,th,into);
                for(register int j=1;j<=n;j++)
                {
                    ins(i,sa[i-1][j],th,num,into);
                    if(f[i-1][j]>=0) 
                        f[i][into]=max(f[i][into],f[i-1][j]+num);
                }
            }
        }
        int ans=0;
        for(register int i=0;i<=n;i++) ans=max(ans,f[m][i]+i*a[1]);
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    百度面试题:把数组排成最小的数
    面试题:在O(1)时间删除链表结点
    从第一字符串中删除第二个字符串中所有的字符
    在一个字符串中找到第一个只出现一次的字符
    大整数运算
    输出1到最大的N位数
    删除字符串中的数字并压缩字符串
    排列 或组合问题的解法(包含回溯法)
    卡特兰数(Catalan)简介
    编程之美-分层遍历二叉树
  • 原文地址:https://www.cnblogs.com/FallDream/p/codeforces402.html
Copyright © 2020-2023  润新知