【矩陣快速冪】【狀態壓縮】【動態規劃】lydsy4000 [TJOI2015]棋盤
阿新 • • 發佈:2018-12-22
4000: [TJOI2015]棋盤
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 643 Solved: 314
[Submit][Status][Discuss]
Description
Input
輸入資料的第一行為兩個整數N,M表示棋盤大小。第二行為兩個整數P,K,
表示攻擊範圍模板的大小,以及棋子在模板中的位置。接下來三行,
每行P個數,表示攻擊範圍的模版。每個數字後面一個空格。
Output
一個整數,表示可行方案Mod 2 ^32
Sample Input
2 2
3 1
0 1 0
1 1 1
0 1 0
Sample Output
7
HINT
1<=N<=10^6,1<=M<=6
這兩天學習和程式碼的狀態都很混亂,感覺是一個陣痛期。這題本身不難,當做一個模板記錄題吧。
題意很顯然(需要注意題目中的行列都是從0開始標號的)。注意到資料範圍n很大而m很小,猜測出是dp,然後可以用1<<m個數字的二進位制代表所有狀態。由於每次狀態轉移方式固定不變,所以可以先預處理出轉移矩陣,再用矩陣快速冪來進行加速。
預處理時,我們先判斷兩個狀態本身是否合法(同一行不互相攻擊),然後對合法的狀態之間嘗試連邊,使得兩個狀態沒有棋子互相攻擊。
矩陣乘法時,答案應為AF^n的各個元素和,其中A是一個只有無棋子狀態為1,其餘狀態為0的1(1<<m)的矩陣。為了方便,我們直接把第一行的和求出來就行了。
另外,題目裡要求對1LL<<32取模,可以直接對unsigned溢位,進一步,可以直接對int溢位,輸出時轉換為unsigned即可,這是因為int是按照補碼儲存的,本質就是一個unsigned。
#include<cstdio>
#define bit(x,i) (x>>i-1&1)
using namespace std;
struct Matrix
{
int P,Q,M[65][65];
inline void operator = (const Matrix &t)
{
P=t.P;
Q=t.Q;
for(int i=1;i<=P;i++)
for(int j=1;j<=Q;j++)
M[i][j]=t.M[i][j];
}
void times(Matrix &a, Matrix &b)
{
if(a.Q!=b.P)
return ;
P=a.P;
Q=b.Q;
for(int i=1;i<=P;i++)
for(int j=1;j<=b.Q;j++)
{
M[i][j]=0;
for(int k=1;k<=a.Q;k++)
M[i][j]+=a.M[i][k]*b.M[k][j];
}
}
void quick_power(int t)
{
Matrix res[2],base[2];
int o=0,p=0;
res[p]=*this;
base[o]=*this;
--t;
while(t)
{
if(t&1)
{
res[p^1].times(res[p],base[o]);
p^=1;
}
base[o^1].times(base[o],base[o]);
o^=1;
t>>=1;
}
*this=res[p];
}
}G;
int n,m,p,k,A[4][7];
unsigned ans;
bool check_valid(int x)
{
for(int i=1;i<=m;i++) //列舉棋子對,看是否攻擊
if(bit(x,i))
for(int j=1;j<=m;j++)
if(i!=j&&bit(x,j))
if(j-i+k>0&&j-i+k<=p&&A[2][j-i+k])
return false;
return true;
}
bool has_edge(int x, int y)
{
for(int i=1;i<=m;i++) //看上面是否攻擊了下面
if(bit(x,i))
for(int j=1;j<=m;j++)
if(bit(y,j))
if(j-i+k>0&&j-i+k<=p&&A[3][j-i+k])
return false;
for(int i=1;i<=m;i++) //看下面是否攻擊了上面
if(bit(y,i))
for(int j=1;j<=m;j++)
if(bit(x,j))
if(j-i+k>0&&j-i+k<=p&&A[1][j-i+k])
return false;
return true;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&p,&k);
k++;
for(int i=1;i<=3;i++)
for(int j=1;j<=p;j++)
scanf("%d",&A[i][j]);
G.P=G.Q=1<<m;
for(int i=1;i<=(1<<m);i++)
if(check_valid(i-1))
for(int j=1;j<=(1<<m);j++)
if(check_valid(j-1))
G.M[i][j]=has_edge(i-1,j-1);
G.quick_power(n);
for(int i=1;i<=(1<<m);i++)
ans+=G.M[1][i];
printf("%u",ans);
return 0;
}