题解 P5188 【[COCI2009-2010#4] PALACINKE】
yukimianyan · · 题解
做法:矩阵优化 DP + 容斥原理。
矩阵优化 DP
先不要考虑商品,写个不管约束条件的 DP。令
这就是朴素的 DP。发现奇妙的
表示
首先把
然后我们要推
(关于如何想出这一个矩阵,个人理解是把矩阵乘法结果中第
还有一个
容斥原理
回归题目,它要我们求出四种商品全买的方案数。直接求不好做,考虑合法方案数 + 不合法方案数 = 总方案数,总方案数显然是安娜在图上随便走的方案数,所以第一步把题意转化成不合法方案数。
令
由于这是
实现
- 判断二进制数
t,s 在子集意义下是否有t\subseteq s ,可以用(~s&t)==0实现。 - 构造
I_1 矩阵时暴力计算f_{1,u} 的方案数。 - 观察到这题模数很小,那么矩阵可以开
int,算完一个值统一取模,以减少常数(需要跑16 次矩阵快速幂)。 - 务必搞清楚容斥系数!
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int popcount(int x){return (x&1)+(x>>1&1)+(x>>2&1)+(x>>3&1);}
const int P=5557;
template<int N,int M,class T=int> struct graph{
int head[N+10],nxt[M*2+10],cnt;
struct edge{
int u,v;T w;
edge(int u=0,int v=0,T w=0):u(u),v(v),w(w){}
} e[M*2+10];
graph(){memset(head,cnt=0,sizeof head);}
edge operator[](int i){return e[i];}
void add(int u,int v,T w=0){e[++cnt]=edge(u,v,w),nxt[cnt]=head[u],head[u]=cnt;}
void link(int u,int v,T w=0){add(u,v,w),add(v,u,w);}
};
template<int N,int M,class T=LL> struct matrix{
T a[N+3][M+3];
matrix(bool f=0){
memset(a,0,sizeof a);
if(f) for(int i=1;i<=N;i++) a[i][i]=1;
}
T* operator[](int i){return a[i];}
void print(int n,int m){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
printf("%d%c",a[i][j]," \n"[j==m]);
}
}
}
};
template<int N,int M,int R,class T=LL> matrix<N,R,T> operator*(matrix<N,M,T> a,matrix<M,R,T> b){
matrix<N,R,T> c=0;
for(int i=1;i<=N;i++){
for(int j=1;j<=R;j++){
for(int k=1;k<=M;k++){
c[i][j]+=a[i][k]*b[k][j];
}
c[i][j]%=P;
}
}
return c;
}
template<int N,class T=LL> matrix<N,N,T> operator^(matrix<N,N,T> a,LL b){
matrix<N,N,T> ans=1;
for(;b;b>>=1,a=a*a) if(b&1) ans=ans*a;
return ans;
}
int n,m,t,tb[128];
char buf[110];
graph<30,510,int> g;
matrix<60,60,int> o;
matrix<1,60,int> e;
int has(char *s){int res=0;for(int i=1,len=strlen(s+1);i<=len;i++) res+=tb[s[i]];return res;}
void binprt(int x){for(int i=3;i>=0;i--) putchar((x>>i&1)+'0');}
int solve(int sta){
o=0,e=0;
e[1][1]=1,o[n*2+1][n*2+1]=o[n+1][n*2+1]=1;//e={0,1}
for(int i=1;i<=n;i++) o[i+n][i]=1;
for(int i=1;i<=g.cnt;i++){
//(u,v)=>f[n][u]=f[n-1][v]+f[n-2][v]
int u=g[i].u,v=g[i].v;
o[u+n][v+n]++;
if(u==1) e[1][v+n]++;
if((~sta&g[i].w)==0) o[u][v+n]++;
}
return (e*(o^t))[1][n*2+1];
}
int main(){
// #ifdef LOCAL
// freopen("input.in","r",stdin);
// #endif
tb['B']=1,tb['J']=2,tb['M']=4,tb['P']=8;
scanf("%d%d",&n,&m);
for(int i=1,u,v;i<=m;i++) scanf("%d%d%s",&u,&v,buf+1),g.add(u,v,has(buf));
scanf("%d",&t);
int all=solve(15),res=0;
for(int i=1;i<=15;i++) res+=solve(15-i)*(popcount(i)&1?1:-1);//,printf("ban=%d,ans%c=%d\n",i,"-+"[popcount(i)&1],solve(15-i));
// printf("all=%d,res=%d\n",all,res);
printf("%d\n",((all-res)%P+P)%P);
return 0;
}
/*
t in s => ~s&t==0
*/
复杂度: