【容斥原理+状态压缩】zjoi2009 多米诺骨牌


转送门:zjoi2009 多米诺骨牌

 

 

彻彻底底的被这道题虐了,想了一个月没想出来,最后和宇宙大总统一起强肯了2个小时标程算是看懂了。。

首先抛开这道题的那个奇怪的限制(没行列没有骨牌跨过),一个赤裸裸的骨牌覆盖我都不不知道怎么做啊!!

先看下赤裸裸的骨牌覆盖怎么做:

一般人的反映就会想到状态压缩DP,没错

状态为F【I,J】表示第i行的状态为J的方案数>>空间复杂度O(N*2^N),转移O(2^N),写的好的话可以做到时间复杂度为O(N*3^N),TLE!

实际上有着么一种更加好写方法,类似CDQ论文中所说的轮廓线,这样设计状态>>F【I,J,K】表示从(i,1)到(i,j-1)、从(i-1,j)到(i-1,m)的状态为K的方案数。这样转移的复杂度为O(1),看上去就可以解决这道题了。

 

我们利用如上所述的方法DP出A【I,J,K,L】表示子矩形(I,J,K,L)的一顿乱放的方案数。

现在在继续考虑怎么完成那个奇怪的限制,我们需要请出容斥原理大侠。。

 

由于横竖都有限制,而容斥原理又不好扩展到2维,所以我们需要在一维上进行容斥原理,在另外一维进行DP,这个DP也比较巧妙,需要用到补集转化的思想,感觉和POJ男人八题的那个啥挺像的(不记得了,应该是求一个点有区别的无向图的联通图个数)。

 

据说H8OJ解除了pas的“四相封印”,不过我交上去还是垫底了。。。

从QZC神哪里学来的几个很搞笑的东西:

定义一个数组指针,比如

a:^array[0..100] of longint;

b:array[0..100] of longint;

 

用这么一个玩意:a:=@b;

然后a^实际上就是b这个数组了,在做高维DP的时候如果你写个F【I,J,K,L,。。。。。。】估计程序会很恐怖,可以用这个来优化下代码量,而且速度估计也会快一点。

 

program ex4; const mo=19901013; type arr=array[0..1 shl 15] of longint; var a:array[0..15,0..15,0..15,0..15] of longint; f:array[0..16,0..15] of arr; g,c:array[0..15] of longint; b:array[0..15] of string; n,m,i,l,r,u,j,t,k,mx,ans:longint; x,y:^arr; tmp:int64; begin readln(n,m); for i:=1 to n do readln(b[i]); for l:=1 to m do for r:=l to m do for u:=1 to n do begin mx:=1<<(r-l+1)-1; for i:=0 to mx do f[u,l,i]:=0; f[u,l,mx]:=1; for i:=u to n do for j:=l to r do begin x:=@f[i,j]; if j=r then y:=@f[i+1,l] else y:=@f[i,j+1]; for k:=0 to mx do y^[k]:=0; if b[i,j]='x' then for k:=0 to mx do inc(y^[k or 1<<(j-l)],x^[k]) else for k:=0 to mx do begin while x^[k]>=mo do dec(x^[k],mo); if not odd(k>>(j-l)) then inc(y^[k or 1<<(j-l)],x^[k]); if (j>l)and (not odd(k>>(j-l-1))) then inc(y^[k or 3<<(j-l-1)],x^[k]); inc(y^[k and not (1<<(j-l))],x^[k]); end; if j=r then for k:=0 to mx do begin inc(a[u,i,l,r],y^[k]); while a[u,i,l,r]>=mo do dec(a[u,i,l,r],mo); end; end; end; for i:=0 to 1<<(m-1)-1 do begin t:=0; for j:=0 to m-2 do if odd(i>>j) then begin inc(t);c[t]:=j+1; end; inc(t);c[t]:=m;g[0]:=1; for j:=1 to n do for k:=0 to j-1 do begin tmp:=1; for t:=1 to t do tmp:=tmp*a[k+1,j,c[t-1]+1,c[t]] mod mo; if k=0 then g[j]:=tmp else g[j]:=(g[j]-tmp*g[k])mod mo; end; if odd(t) then inc(ans,g[n]) else dec(ans,g[n]); ans:=(ans+mo)mod mo; end; writeln(ans); end. 

 

 

 

你可能感兴趣的:(c,优化,String,扩展)