首先状压Dp应该都知道吧(然而yjq直接容斥强势艹过...),那么我们来优化状态,首先考虑不可能匹配成功的状态,把它们缩到一个状态,我们发现,哇!一下子少了好多状态!大概从几百万变成了5w-15w左右,但是Dp是状态数^2的,这样子肯定不行...然后一个鬼畜的优化就来了,我把与第一行匹配开始点相同的状态缩成一个状态(比如两个状态i和j,如果它
们和模板串都是从{1,5,6}开始可以匹配,那么其实它们可以当做一个状态,但是注意又可以匹配模板第一行,又可以匹配模板第二行的状态,那么它们和两行匹配的位置都必须相同.),然后发现有一维的状态变成了100+的数量,然后省选就90了,然后bzoj就卡着时限过了....估计用AC自动机进行中途的匹配过程可以快10倍左右吧(然而我这代码已经7k了,再多一点就....)...(还有一个脑洞不知道行不行,因为当时考试没足够时间,现在又与模板第一行匹配的状态k个,与第二行匹配的状态c个,之前考场上的做法只缩了k,但是c好像也可以缩一缩?要是c也能缩那估计就可以虐标算了.)
#include <iostream> #include <cstring> #include <cstdlib> #include <string> #include <cstdio> #include <algorithm> #include <cmath> #include <ctime> #define Mod 1000000007 using namespace std; struct node {int op;int val;int num;int k;int pos[20];int id;int pox[20]; };node Dp[600010]; struct edge{int next;int to; };edge bian[10000010]; int first[600010],size = 0,cnt = 0,m,c,A[3][20],s[100]; int top = 0,v[100],pos[600010],queue[600010],tot = 0,n; int p,Ans,f[110][10010][2],P[20],Q[20]; bool exist[600010],visit[600010]; void inser(int x,int y) { size ++; bian[size].to = y; bian[size].next = first[x]; first[x] = size; } void check1(int x) { cnt = 0; for(int i = 0;i <= m - c;i ++) { bool F = true; for(int j = 1;j <= c;j ++) if(A[1][j] != s[i + j - 1]) {F = false;break;} if(F == true) v[ ++cnt] = i; } if(cnt == 0) return ; exist[x] = true; top ++; Dp[top].k = 0; Dp[top].num = 1; Dp[top].val = x; Dp[top].op = cnt; for(int i = 1;i <= Dp[top].op;i ++) Dp[top].pos[i] = v[i]; if(visit[x] == true) { cnt = 0; for(int i = 0;i <= m -c;i ++) { bool F = true; for(int j = 1;j <= c;j ++) if(A[2][j] != s[i + j - 1]) {F = false;break;} if(F) v[ ++cnt] = i; } Dp[top].id = cnt; for(int i = 1;i <= Dp[top].id;i ++) Dp[top].pox[i] = v[i]; } else Dp[top].id = 0; } void check2(int x) { for(int i = 0;i <= m - c;i ++) { bool F = true; for(int j = 1;j <= c;j ++) if(A[2][j] != s[i + j - 1]) {F = false;break;} if(F == true) {visit[x] = true;queue[ ++tot] = x;return ;} } } bool check(int x,int y) { //cerr<<"HH"<<endl; for(int i = 0;i <= m - 1;i ++) P[i] = x % 3,x /= 3; for(int i = 0;i <= m - 1;i ++) Q[i] = y % 3,y /= 3; for(int i = 0;i <= m - c;i ++) { bool F = true; for(int j = 1;j <= c;j ++) if(A[1][j] != P[i + j - 1] || A[2][j] != Q[i + j - 1]) {F = false;break;} if(F) return true; } return false; } bool comp(const node &x,const node &y) { if(x.op != y.op) return x.op < y.op; if(x.id != y.id) return x.id < y.id; for(int i = 1;i <= x.op;i ++) if(x.pos[i] != y.pos[i]) return x.pos[i] < y.pos[i]; for(int i = 1;i <= x.id;i ++) if(x.pox[i] != y.pox[i]) return x.pox[i] < y.pox[i]; return x.val < y.val; } bool same(node A,node B) { if(A.op != B.op) return false; if(A.id != B.id) return false; for(int i = 1;i <= A.op;i ++) if(A.pos[i] != B.pos[i]) return false; for(int i = 1;i <= A.id;i ++) if(A.pox[i] != B.pox[i]) return false; return true; } bool what(const int &x,const int &y) { return pos[x] < pos[y]; } int main() { freopen("alphago.in","r",stdin); freopen("alphago.out","w",stdout); scanf("%d%d%d%d",&n,&m,&c,&p); while(p --) { Ans = 0;size = 0;top = 0;tot = 0; memset(exist,false,sizeof(exist)); memset(visit,false,sizeof(visit)); memset(first,0,sizeof(first)); memset(queue,0,sizeof(queue)); memset(pos,0,sizeof(pos)); memset(Dp,0,sizeof(Dp)); for(int i = 1;i <= 2;i ++) for(int j = 1;j <= c;j ++) { char c = 'd'; while(c != 'B' && c != 'W' && c != 'X') c = getchar(); if(c == 'B') A[i][j] = 1; if(c == 'W') A[i][j] = 2; if(c == 'X') A[i][j] = 0; } int N = 1; for(int i = 1;i <= m;i ++) N = N * 3; for(int i = 0;i <= N - 1;i ++) { int M = i; for(int j = 0;j <= m - 1;j ++) { s[j] = M % 3; M /= 3; } check2(i); check1(i); } int O = N - top; sort(Dp + 1,Dp + top + 1,comp); int Z = top;top = 0; for(int i = 1;i <= Z;i ++) if(i == 1 || !same(Dp[i],Dp[i - 1])) Dp[ ++top] = Dp[i],pos[Dp[i].val] = top; else Dp[top].num ++,pos[Dp[i].val] = top; sort(queue + 1,queue + tot + 1,what); //cerr<<clock()<<endl; for(int i = 1;i <= top;i ++) { for(int j = 1;j <= tot;j ++) if(check(Dp[i].val,queue[j])) if(!exist[queue[j]]) Dp[i].k ++; else if(j == 1 || pos[queue[j]] != pos[queue[j - 1]]) inser(i,pos[queue[j]]); }memset(f,0,sizeof(f)); for(int i = 1;i <= top;i ++) f[1][i][0] = Dp[i].num; f[1][top + 1][0] = O; cerr<<size<<endl; //cerr<<clock()<<endl; for(int i = 1;i <= n - 1;i ++) { for(int j = 1;j <= top;j ++) { int Add = (1ll * f[i][j][0] * Dp[j].k) % Mod; f[i + 1][top + 1][1] += Add; if(f[i + 1][top + 1][1] > Mod) f[i + 1][top + 1][1] -= Mod; Add = (1ll * f[i][j][0] * (1ll * O - 1ll * Dp[j].k)) % Mod; f[i + 1][top + 1][0] += Add; if(f[i + 1][top + 1][0] > Mod) f[i + 1][top + 1][0] -= Mod; Add = (1ll * f[i][j][1] * O) % Mod; f[i + 1][top + 1][1] += Add; if(f[i + 1][top + 1][1] > Mod) f[i + 1][top + 1][1] -= Mod; for(int u = first[j];u;u = bian[u].next) { Add = (1ll * f[i][j][0] * Dp[bian[u].to].num) % Mod; f[i + 1][bian[u].to][1] += Add; if(f[i + 1][bian[u].to][1] > Mod) f[i + 1][bian[u].to][1] -= Mod; /* Add = (1ll * f[i][j][1] * Dp[bian[u].to].num) % Mod; f[i + 1][bian[u].to][1] += Add; if(f[i + 1][bian[u].to][1] > Mod) f[i + 1][bian[u].to][1] -= Mod;*/ f[i + 1][bian[u].to][0] -= Add; if(f[i + 1][bian[u].to][1] < 0) f[i + 1][bian[u].to][0] += Mod; } for(int k = 1;k <= top;k ++) { Add = (1ll * f[i][j][0] * Dp[k].num) % Mod; f[i + 1][k][0] += Add; if(f[i + 1][k][0] > Mod) f[i + 1][k][0] -= Mod; Add = (1ll * f[i][j][1] * Dp[k].num) % Mod; f[i + 1][k][1] += Add; if(f[i + 1][k][1] > Mod) f[i + 1][k][1] -= Mod; } } for(int k = 1;k <= top;k ++) { int Add; Add = (1ll * f[i][top + 1][0] * Dp[k].num) % Mod; f[i + 1][k][0] += Add; if(f[i + 1][k][0] > Mod) f[i + 1][k][0] -= Mod; Add = (1ll * f[i][top + 1][1] * Dp[k].num) % Mod; f[i + 1][k][1] += Add; if(f[i + 1][k][1] > Mod) f[i + 1][k][1] -= Mod; } int Add; Add = (1ll * f[i][top + 1][0] * O) % Mod; f[i + 1][top + 1][0] += Add; if(f[i + 1][top + 1][0] > Mod) f[i + 1][top + 1][0] -= Mod; Add = (1ll * f[i][top + 1][1] * O) % Mod; f[i + 1][top + 1][1] += Add; if(f[i + 1][top + 1][1] > Mod) f[i + 1][top + 1][1] -= Mod; } //cerr<<clock()<<endl; for(int i = 1;i <= top + 1;i ++) Ans = (Ans + f[n][i][1]) % Mod; printf("%d\n",Ans); } return 0; }
然而又瞬间秒写了另外一个优化,就是对于第二层状态判一下相同的状态剪掉...(这还只是不完全剪枝,果然又快了一倍多....)
bool yiyang(const int &x,const int &y) { if(pos[x] == 0 || pos[y] == 0) return false; return pos[x] == pos[y]; } sort(queue + 1,queue + tot + 1,what); tot = unique(queue + 1,queue + tot + 1,yiyang) - queue - 1;
在处理完Dp之后其实就加了这么一句小小的话....