●BZOJ 2669 [cqoi2012]局部极小值
题链:
http://www.lydsy.com/JudgeOnline/problem.php?id=2669
题解:
容斥,DP,DFS
先看看 dp 部分:
首先呢,X的个数不会超过 8个。
个数很少,所以考虑状压,把需要填 X的那几个位置状压为二进制10表示对应的那个X位置是否已经填数。
同时填的数互不重复,考虑从小填到大。
令 cnt[S] 表示除了不在集合 S 里的 X 位置及其周围的位置,剩下的位置个数。
定义 dp[i][S]表示从小到大填数填完了i这个数,且已经填了的 S 这个集合里的 X 位置的方案数。
转移:依次去填数 1~N*M,每次有两种选择:
1).把这个数填在 某个 X 位置(枚举一个 k表示第 k个 X 位置填当前数)
dp[i][S]+=dp[i-1][S^(1<<(k-1))]
2).把这个数填在非 X 位置,那么填的位置有 cnt[S]-(i-1) 种。
dp[i][s]+=dp[i-1][s]*(cnt[s]-(i-1)) (好好理解一下这个转移)
这样 dp 可以保证那些给出的 X 位置一定是局部最小值,
因为第二种转移的填数位置都不能填在还没有填数的 X 位置的周围。
所以就完了么?
当然还没有,尽管我们保证了给出的 X 位置一定是局部最小值,
但是没有保证非 X位置一定不是非局部最小值。即,求出来的 dp[N*M][all_S(全集)]的意思是至少all_S集合里的 X位置为局部最小值的方案数。
所以容斥如下:
ANS = 至少多填了0个局部最小值的方案数(dp[N*M][all_S])
-至少多填了1个局部最小值的方案数
+至少多填了2个局部最小值的方案数
-....+ ....
这些用于容斥的方案数的求法:
DFS 搜索出哪些非 X 位置还可以改为 X ,
然后对于每一种新的填法,去跑一遍上述的dp即可求得对应的方案数。
代码:
#include<cstdio> #include<cstring> #include<iostream> #define _ % mod #define filein(x) freopen(#x".in","r",stdin); #define fileout(x) freopen(#x".out","w",stdout); using namespace std; const int mv[9][2]={{0,0},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}}; const int mod=12345678; char mp[10][10]; int N,M,ANS; int solve(){ static bool vis[10][10]; static int dp[30][1<<8],cnt[1<<8],x[10],y[10],tot,tmp; tot=0; memset(dp,0,sizeof(dp)); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) if(mp[i][j]=='X') tot++,x[tot]=i,y[tot]=j; for(int s=0;s<1<<tot;s++){ tmp=0; memset(vis,0,sizeof(vis)); for(int i=1;i<=tot;i++) if(!(s&(1<<(i-1)))) for(int k=0;k<9;k++) vis[x[i]+mv[k][0]][y[i]+mv[k][1]]=1; for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) if(!vis[i][j]) tmp++; cnt[s]=tmp; } dp[0][0]=1; for(int i=1;i<=N*M;i++) for(int s=0;s<1<<tot;s++){ dp[i][s]=(1ll*dp[i][s]+1ll*dp[i-1][s]*max(cnt[s]-(i-1),0)_)_; for(int k=1;k<=tot;k++) if(s&(1<<(k-1))) dp[i][s]=(1ll*dp[i][s]+dp[i-1][s^(1<<(k-1))])_; } return dp[N*M][(1<<tot)-1]; } void dfs(int x,int y,int t){ if(y==M+1){dfs(x+1,1,t);return;} if(x==N+1){ int tmp=solve(); if(t&1) tmp=(-1ll*tmp+mod)_; ANS=((1ll*ANS+tmp)_+mod)_; return; } dfs(x,y+1,t); bool fg=1; for(int k=0;k<9;k++) if(mp[x+mv[k][0]][y+mv[k][1]]=='X') fg=0; if(fg){ mp[x][y]='X'; dfs(x,y+1,t+1); mp[x][y]='.'; } } int main() { scanf("%d%d",&N,&M); for(int i=1;i<=N;i++) scanf("%s",mp[i]+1); dfs(1,1,0); printf("%d",ANS); return 0; }
Do not go gentle into that good night.
Rage, rage against the dying of the light.
————Dylan Thomas