JZOJ5416. 【NOIP2017提高A组集训10.22】密码 DP
2017-10-23 17:29
399 查看
Description
现在身为校庆志愿者的小C正在引导校友们到他们集合的教室。终于,忙了一段时间的他可以休息一会儿了。这时,旁边一位老校友的话吸引到了他。“我后来当了一名探险家,有一次,我来到了一个地方,在正前方有一扇门,旁边写着一行文字:’现在给你前m个字符串G,有一个拼接规律T,它是一个长度为m的一个排列,你要把现在已经得到的最后m个字符串按照T的顺序拼接起来,得到一个新的字符串,用这种方法,你可得到第n个字符串P,再给你另外一个字符串S,则S在P中出现的次数就是这个门的密码…’”听到这里,小C陷入了沉思:到底密码是多少呢?不过由于小C比较讨厌大数,他只想知道这个密码除以(10^9+7)的余数。你能帮帮他吗?
Input
第一行包含一个整数m,表示开始字符串的个数(排列长度)。
接下来行,每行一个字符串,表示前m个字符串G,保证字符串的长度均相同。
接下来一行,包含m个正整数,表示T。
接下来一行,包含一个整数q,表示数据组数。
对于每一组数据,包含一个字符串和一个整数,分别表示S及n。
Output
输出总共行,每一个询问对应一个答案。
Sample Input
3
ab
ac
ca
2 3 1
3
ac 2
b 1
a 5
Sample Output
1
1
5
Data Constraint
对于20%的数据,m<=2 ,|G|<=6 ,n<=15 。
对于30%的数据, m<=5 ,|G|<=50 ,n<=400 。
对于50%的数据,m<=5 ,|G|<=50 ,n<=20000 。
对于80%的数据, m<=9 ,|G|<=100 ,n<=10^6。
对于100%的数据, m<=9 ,|G|<=200 ,n<=10^9,q<=7,|S|<=|G|。
很鬼畜的题目,比赛的时候只会50,然后还因为传递地址等各种乱七八糟的c++语法然后挂了。。
80分应该是比较好大的,100分比较复杂我就放弃了,所以这里只讲80分的方法。
注意到n<=10^6,那么我们可以线性dp。
首先我们知道,如果要把m个连起来,那么他们的贡献就是原来每一块的贡献和连接处的贡献,然后线性推出f[i]。
先把每一块的贡献预处理出来直接算进f里面,然后连接处的贡献的话就设suf[i][j],pre[i][j],pre[i,j]表示第i个字符串的前j个字符和s的后j个字符能否匹配,suf[i][j]表示第i的字符串的后j个字符串和s的前j个字符串能否匹配,然后在预处理一些东西:
预处理link[i,j]表示把第i个串接到第j个串前面会多出现多少次s
记录form[i,0/1]分别表示第i个串前面/后面是哪个字符串
然后直接转移即可。
具体的话就是
f[i]+=link[from[i−m−1+p[j]][1]][from[i−m−1+p[j+1]][0]]
很好理解啦。
ymw真的强QAQ。
现在身为校庆志愿者的小C正在引导校友们到他们集合的教室。终于,忙了一段时间的他可以休息一会儿了。这时,旁边一位老校友的话吸引到了他。“我后来当了一名探险家,有一次,我来到了一个地方,在正前方有一扇门,旁边写着一行文字:’现在给你前m个字符串G,有一个拼接规律T,它是一个长度为m的一个排列,你要把现在已经得到的最后m个字符串按照T的顺序拼接起来,得到一个新的字符串,用这种方法,你可得到第n个字符串P,再给你另外一个字符串S,则S在P中出现的次数就是这个门的密码…’”听到这里,小C陷入了沉思:到底密码是多少呢?不过由于小C比较讨厌大数,他只想知道这个密码除以(10^9+7)的余数。你能帮帮他吗?
Input
第一行包含一个整数m,表示开始字符串的个数(排列长度)。
接下来行,每行一个字符串,表示前m个字符串G,保证字符串的长度均相同。
接下来一行,包含m个正整数,表示T。
接下来一行,包含一个整数q,表示数据组数。
对于每一组数据,包含一个字符串和一个整数,分别表示S及n。
Output
输出总共行,每一个询问对应一个答案。
Sample Input
3
ab
ac
ca
2 3 1
3
ac 2
b 1
a 5
Sample Output
1
1
5
Data Constraint
对于20%的数据,m<=2 ,|G|<=6 ,n<=15 。
对于30%的数据, m<=5 ,|G|<=50 ,n<=400 。
对于50%的数据,m<=5 ,|G|<=50 ,n<=20000 。
对于80%的数据, m<=9 ,|G|<=100 ,n<=10^6。
对于100%的数据, m<=9 ,|G|<=200 ,n<=10^9,q<=7,|S|<=|G|。
很鬼畜的题目,比赛的时候只会50,然后还因为传递地址等各种乱七八糟的c++语法然后挂了。。
80分应该是比较好大的,100分比较复杂我就放弃了,所以这里只讲80分的方法。
注意到n<=10^6,那么我们可以线性dp。
首先我们知道,如果要把m个连起来,那么他们的贡献就是原来每一块的贡献和连接处的贡献,然后线性推出f[i]。
先把每一块的贡献预处理出来直接算进f里面,然后连接处的贡献的话就设suf[i][j],pre[i][j],pre[i,j]表示第i个字符串的前j个字符和s的后j个字符能否匹配,suf[i][j]表示第i的字符串的后j个字符串和s的前j个字符串能否匹配,然后在预处理一些东西:
预处理link[i,j]表示把第i个串接到第j个串前面会多出现多少次s
记录form[i,0/1]分别表示第i个串前面/后面是哪个字符串
然后直接转移即可。
具体的话就是
f[i]+=link[from[i−m−1+p[j]][1]][from[i−m−1+p[j+1]][0]]
很好理解啦。
ymw真的强QAQ。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cstring> #define fo(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) using namespace std; const int N=2e4+5; const int mo=1e9+7; int n,m; int f[N],w[20],link[20][20]; int p[20],len,from[N][2]; bool suf[205][205],pre[205][205]; char ch[205],s[20][205]; inline int solve() { int l=strlen(ch+1); fo(i,1,m) { w[i]=0; fo(j,1,len-l+1) { int flag=1; fo(k,1,l) if (ch[k]!=s[i][j+k-1]) { flag=0; break; } w[i]+=flag; } fo(j,1,l) { int flag=1; pre[i][j]=suf[i][j]=0; fo(k,1,j) if (ch[l-j+k]!=s[i][k]) { flag=0; break; } pre[i][j]=flag,flag=1; fo(k,1,j) if (ch[j-k+1]!=s[i][len-k+1]) { flag=0; break; } suf[i][j]=flag; } } fo(i,1,m) fo(j,1,m) { link[i][j]=0; fo(k,1,len-1) if (suf[i][k]&&pre[j][l-k])link[i][j]++; } fo(i,1,m)f[i]=w[i],from[i][0]=from[i][1]=i; fo(i,m+1,n) { f[i]=0; int tmp=i-m-1; fo(j,1,m)f[i]+=f[i-j],f[i]%=mo; fo(j,1,m-1) f[i]+=link[from[tmp+p[j]][1]][from[tmp+p[j+1]][0]], f[i]%=mo; from[i][0]=from[tmp+p[1]][0]; from[i][1]=from[tmp+p[m]][1]; } if (f ==0)f =21; return f ; } int main() { freopen("password.in","r",stdin); freopen("password.out","w",stdout); scanf("%d",&m); fo(i,1,m)scanf("%s",s[i]+1); len=strlen(s[1]+1); fo(i,1,m)scanf("%d",&p[i]); int q; scanf("%d",&q); while (q--) { scanf("%s%d",ch+1,&n); printf("%d\n",solve()); } return 0; }
相关文章推荐
- 【JZOJ 5416】【NOIP2017提高A组集训10.22】密码
- JZOJ5411. 【NOIP2017提高A组集训10.22】友谊 DP
- JZOJ5415. 【NOIP2017提高A组集训10.22】公交运输 DP
- 【JZOJ5415】【NOIP2017提高A组集训10.22】[斜率优化]公交运输
- JZOJ 5410. 【NOIP2017提高A组集训10.22】小型耀斑
- [JZOJ5413]【NOIP2017提高A组集训10.22】清兰
- 【JZOJ5411】【NOIP2017提高A组集训10.22】友谊
- jzoj5408 【NOIP2017提高A组集训10.21】Dark (巧设状态的DP)
- JZOJ5415. 【NOIP2017提高A组集训10.22】公交运输
- JZOJ 5415. 【NOIP2017提高A组集训10.22】公交运输
- JZOJ5436. 【NOIP2017提高A组集训10.30】Group DP
- JZOJ5414. 【NOIP2017提高A组集训10.22】幸运值
- 5332. 【NOIP2017提高A组模拟8.23】密码 AC自动机+数位DP
- 5414. 【NOIP2017提高A组集训10.22】幸运值
- 【JZOJ 5410】【NOIP2017提高A组集训10.22】小型耀斑
- [JZOJ5411]【NOIP2017提高A组集训10.22】友谊
- JZOJ 5414. 【NOIP2017提高A组集训10.22】幸运值
- [JZOJ5415]【NOIP2017提高A组集训10.22】公交运输
- 【JZOJ5413】【NOIP2017提高A组集训10.22】清兰
- 【JZOJ 5413】【NOIP2017提高A组集训10.22】清兰