时间限制 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 ≤ 10 5 ,0 ≤ |w|,|xi| ≤ 10 5 。
考虑n个果冻怪,可以看成多个果冻怪的影响相加,那么首先考虑1个果冻怪的情形。画出1个果冻怪的分裂情况,则很容易看出是一个杨辉三角形的形式。结合w、x、T之间的关系不难得出结论。由于需要计算的组合数全部在杨辉三角的同一层,用O(n)的循环预处理出该层组合数即可。
当abs(w-x)与T奇偶性不同时不会增加果冻怪。当w > x+T或w < x-T时不会增加果冻怪。
注意数据规模,不能使用Lucas定理。在这样的规模下Lucas退化成了O(n)的暴力求组合数。容易因为习惯性思维和模质数的条件写成Lucas超时。
#include
#define ll long long
const 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||Wcontinue;
Ans=(Ans+C[T+1-(((T+2)>>1)+(tmp>>1))]*Y%mod)%mod;
}
printf("%lld",Ans);
}
时间限制 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 ≤ 10 5 。
注意到本题的空间限制比较大,是其他题目的三倍,再考虑到本题一定会涉及字符串的匹配,那么字典树是比较理想的选择。
显然可以看成背包动规。令f[i]表示解密完前i个字母时的最小单词数,那么显然得到状态转移方程:
f[i]=min(f[j]+1),j<i,j+1到i之间为一个字典中的单词
那么我们从密文的第一位开始,将i~n之间的字符串在字典树中进行匹配,一边匹配一边更新f数组即可。由于单词长度不超过50,那么字典树的深度最多也是50,所以最多计算50*10 5 次。
#include
#include
#define Min(x,y) ((x
int N,M,Hash[500];
int f[100005];
int rt=1,tot=1,Son[5000005][10];
bool num[5000005];
//trie
char s[100],ask[100005];
void Insert()
{
int len,i,p=rt,t;
scanf("%s",s);len=strlen(s);
for(i=0;iif (!Son[p][t])Son[p][t]=++tot;
}
num[p]=true;
}
void Find(int l)
{
int p=rt,i,t;
for(i=l;i48;
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]);
}
遇到的构造类第一题。比赛时完全没有思路,就写了个比较精巧的骗分,竟然还骗到了90分。下面还是简要介绍一下骗分算法:
再将原数列排序后,用对应位置的差去填充上面的空位即可。这样的骗分可以保证出现 ⌊n2⌋ 个不同的元素,而且很好写,所以是比较划得来的。
90分骗分代码:
#include
#include
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
#include
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的骗分想起来还是比较机智的,以后拿到没见过的题型也要尽量得分。