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()!='\n')
        {
            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\n%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\n",A[i].x,A[i].y);
    for(int i=cnt;i>pre;i--)    printf("%d %d\n",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;
}

 

posted @ 2017-02-26 23:48  FallDream  阅读(766)  评论(0编辑  收藏  举报