一个鬼畜的做法...(估计没人看,看了也没人写..)(最终优化形态请看最后一个代码)
首先状压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之后其实就加了这么一句小小的话....