2017暑假第二阶段第四场 总结

来源:互联网 发布:极限挑战网络直播地址 编辑:程序博客网 时间:2024/06/11 05:15

T1 果冻怪

时间限制 1s 空间限制 128MB

问题描述

小南和小开在三友路上养了很多只果冻怪。我们可以将三友路想象成一根长度无限的数 轴,在这上面生活着n只果冻怪。每经过一秒,一只果冻怪便会分裂成两只。具体来说,一 只坐标为x的果冻怪,会分裂成两只分别在(x − 1),(x + 1)上的果冻怪,并且原来在x上的果冻怪会消失。 由于生存空间有限,若一个位置上有不少于P只果冻怪,那么会立刻消失 P 只。经过测 定P = 10^9 + 7。 小南和小开想知道在第T秒末,位置w有多少只果冻怪。初始时刻是0秒初。

输入格式

第一行为三个整数,n,T,w。含义如题所述。
接下来 n 行,每行两个整数xi,ci,表示xi位置,有ci只果冻怪。注意xi可能有重复。

输出格式

输出一个非负整数,表示 T 秒末 w 位置上的果冻怪个数

数据范围

对于 100%的数据:1 ≤ n,T,ci ≤ 105,0 ≤ |w|,|xi| ≤ 105


考虑n个果冻怪,可以看成多个果冻怪的影响相加,那么首先考虑1个果冻怪的情形。画出1个果冻怪的分裂情况,则很容易看出是一个杨辉三角形的形式。结合w、x、T之间的关系不难得出结论。由于需要计算的组合数全部在杨辉三角的同一层,用O(n)的循环预处理出该层组合数即可。

当abs(w-x)与T奇偶性不同时不会增加果冻怪。当w > x+T或w < x-T时不会增加果冻怪。

注意数据规模,不能使用Lucas定理。在这样的规模下Lucas退化成了O(n)的暴力求组合数。容易因为习惯性思维和模质数的条件写成Lucas超时。

#include<stdio.h>#define ll long longconst ll mod=1e9+7;ll Ans,N,T,W,X,Y,C[100005];ll ksm(ll a,ll b){    ll ans=1;a%=mod;    while(b)    {        if(b&1)ans=ans*a%mod;        a=a*a%mod;b>>=1;    }    return ans;}int main(){    int i;    ll tmp;    scanf("%lld%lld%lld",&N,&T,&W);    C[0]=1;C[1]=T;    for(i=2;i<=T;i++)C[i]=C[i-1]*(T-i+1)%mod*ksm(i,mod-2)%mod;//预处理    for(i=1;i<=N;i++)    {        scanf("%lld%lld",&X,&Y);        if(W>X)tmp=W-X;        else tmp=X-W;        if((tmp&1)!=(T&1))continue;        if(W>X+T||W<X-T)continue;        Ans=(Ans+C[T+1-(((T+2)>>1)+(tmp>>1))]*Y%mod)%mod;    }    printf("%lld",Ans);}

T2 解密游戏

时间限制 2s 空间限制 384MB

问题描述

小南和小开特别喜欢玩解密游戏,轮到小南加密的时候,由于他的加密方式过于丧心病 狂,所以小开怎么也不能解密成功,于是她来找你帮忙。 密文是一个长度为 n 的数字串,只由 0~9 之间的数字组成。每个小写字母对应 0~9 之 间的一个数字。小南和小开共同拥有一本字典,字典中有 m 个单词,每个单词长度不超过 50。 明文是一个数字,表示最少用多少个单词首尾拼接在一起,使得拼接而成的这个字符串 可以表示密文(也即相同位置的字符串中字母对应数字跟密文相同)。单词可以重复使用。
输出明文,如果无解的话明文为-1。

输入格式

第一行两个正整数 n,m。
第二行有 26 个数字,每个数字是 0~9 之间的数,分别表示字母 a~z 对应的数字。
第三行是长度为 n 的数字串,表示密文。 接下来 m 行,每行一个小写字母串,表示字典中的一个单词。

输出格式

输出一个整数,表示明文

数据范围

对于 30%的数据:1 ≤ n,m ≤ 1000。
对于 100%的数据:1 ≤ n,m ≤ 105


注意到本题的空间限制比较大,是其他题目的三倍,再考虑到本题一定会涉及字符串的匹配,那么字典树是比较理想的选择。

显然可以看成背包动规。令f[i]表示解密完前i个字母时的最小单词数,那么显然得到状态转移方程:

f[i]=min(f[j]+1)j<ij+1i

那么我们从密文的第一位开始,将i~n之间的字符串在字典树中进行匹配,一边匹配一边更新f数组即可。由于单词长度不超过50,那么字典树的深度最多也是50,所以最多计算50*105次。

#include<stdio.h>#include<cstring>#define Min(x,y) ((x<y)?(x):(y))int N,M,Hash[500];int f[100005];int rt=1,tot=1,Son[5000005][10];bool num[5000005];//triechar s[100],ask[100005];void Insert(){    int len,i,p=rt,t;    scanf("%s",s);len=strlen(s);    for(i=0;i<len;i++,p=Son[p][t])    {        t=Hash[s[i]];        if(!Son[p][t])Son[p][t]=++tot;    }    num[p]=true;}void Find(int l){    int p=rt,i,t;    for(i=l;i<N&&p;i++,p=Son[p][t])    {        t=ask[i]-48;        if(num[Son[p][t]])f[i+1]=Min(f[i+1],f[l]+1);    }}int main(){    int i;    scanf("%d%d",&N,&M);    for(i=0;i<26;i++)scanf("%d",&Hash[i+'a']);    scanf("%s",ask);    for(i=1;i<=M;i++)Insert();    memset(f,60,sizeof(f));    f[0]=0;    for(i=1;i<=N;i++)Find(i-1);    if(f[N]==f[100001])puts("-1");    else printf("%d",f[N]);}

T3 分割财产

这里写图片描述

遇到的构造类第一题。比赛时完全没有思路,就写了个比较精巧的骗分,竟然还骗到了90分。下面还是简要介绍一下骗分算法:

首先构造出这样的数列:
这里写图片描述

再将原数列排序后,用对应位置的差去填充上面的空位即可。这样的骗分可以保证出现n2个不同的元素,而且很好写,所以是比较划得来的。

90分骗分代码:

#include<algorithm>#include<stdio.h>using namespace std;int N,data[5005],Hash[5005],Ans[5005][2];int main(){    int i,j=-1;    scanf("%d",&N);    for(i=1;i<=N;i++)scanf("%d",&data[i]),Hash[i]=data[i];    sort(Hash+1,Hash+N+1);    for(i=1;i<=N;i++)    {        if(i&1)j++;        Ans[i][i&1]=Hash[i]-j;        Ans[i][i-1&1]=j;    }    puts("YES");    for(i=1;i<=N;i++)    {        j=lower_bound(Hash+1,Hash+N+1,data[i])-Hash;        printf("%d ",Ans[j][0]);    }    putchar('\n');    for(i=1;i<=N;i++)    {        j=lower_bound(Hash+1,Hash+N+1,data[i])-Hash;        printf("%d ",Ans[j][1]);    }}

正解

将原数列按照大小排序并重新编号,之后分成长度为n3的三段开始构造:

设编号为i。

第一段:将i-1赋给ai,将si-ai赋给bi。
第二段:将i-1赋给bi,将si-bi赋给ai。
第三段:将n-i赋给bi,将si-bi赋给ai。

可以看出,ai的第一段与第三段的值是不会重复的,bi的第二段与第三段的值也是不会重复的(n3-1< si-(2n3-1)),这样就满足了要求。

由此也可以看出,这道题无论如何都有解。因此只输出”NO”连一分也不能骗到。

#include<algorithm>#include<stdio.h>using namespace std;int N,S[5005],Hash[5005],A[5005],B[5005];int main(){    int i,j,K;    scanf("%d",&N);    for(i=1;i<=N;i++)scanf("%d",&S[i]),Hash[i]=S[i];    sort(Hash+1,Hash+N+1);    K=(N+2)/3;    for(i=1;i<=K;i++)    {        A[i]=i-1;        B[i]=Hash[i]-A[i];    }    for(;i<=K*2;i++)    {        B[i]=i-1;        A[i]=Hash[i]-B[i];    }    for(;i<=N;i++)    {        B[i]=N-i;        A[i]=Hash[i]-B[i];    }    puts("YES");    for(i=1;i<=N;i++)    {        j=lower_bound(Hash+1,Hash+N+1,S[i])-Hash;        printf("%d ",A[j]);    }    putchar('\n');    for(i=1;i<=N;i++)    {        j=lower_bound(Hash+1,Hash+N+1,S[i])-Hash;        printf("%d ",B[j]);    }}

总结

T1是简单的组合数公式;T2是trie+DP,T3是构造。

T1思想江化了,看到组合数模一个质数就想到了Lucas定理,却忽略了时间复杂度导致只得了80分;T2做得不错,只是一开始由于字符数组编号0开始存导致了一些编写上的问题,拖慢了速度;T3的骗分想起来还是比较机智的,以后拿到没见过的题型也要尽量得分。

原创粉丝点击