1. 程式人生 > >Xor-sequences CodeForces - 691E || 矩陣快速冪

Xor-sequences CodeForces - 691E || 矩陣快速冪

[1] images temp () const cursor cnblogs 矩陣快速冪 ems

Xor-sequences CodeForces - 691E

題意:在有n個數的數列中選k個數(可以重復選,可以不按順序)形成一個數列,使得任意相鄰兩個數異或的結果轉換成二進制後其中1的個數是三的倍數。求可能形成的不同數列個數(只要選出的數列中,任意兩個元素在原序列中的位置不同,就算作不同的序列,比如在原數列[1,1]中選1個,那麽第一個1和第二個1要分開算)。

方法:

很容易列出dp方程:

dp[k][i]表示取了k個,最後一個在第i位。a[i][j]表示i和j異或結果轉換成二進制後1的個數是否是3的倍數,1表示是,0表示否。

$dp[k][i]=dp[k-1][1]*a[1][i]+...dp[k-1][n]*a[n][i]$

註意,不是$dp[k][i]=dp[k-1][1]*a[1][i]+...+dp[k-1][i-1]*a[i-1][i]$(這道題是可以重復、不按順序選的,這麽寫就是不重復、按順序)

那麽,這樣的算法復雜度就是O(nk),太慢了,需要優化。

從小數據開始:

n=3時:

dp[1][1]=1
dp[1][2]=1
dp[1][3]=1

dp[2][1]=dp[1][1]*a[1][1]+dp[1][2]*a[2][1]+dp[1][3]*a[3][1]
dp[2][2]=dp[1][1]*a[1][2]+dp[1][2]*a[2][2]+dp[1][3]*a[3][2]
dp[2][3]=dp[1][1]*a[1][3]+dp[1][2]*a[2][3]+dp[1][3]*a[3][3]

dp[3][1]=dp[2][1]*a[1][1]+dp[2][2]*a[2][1]+dp[2][3]*a[3][1]
dp[3][2]=dp[2][1]*a[1][2]+dp[2][2]*a[2][2]+dp[2][3]*a[3][2]
dp[3][3]=dp[2][1]*a[1][3]+dp[2][2]*a[2][3]+dp[2][3]*a[3][3]

很容易可以發現:
矩陣1
dp[1][1] dp[1][2] dp[1][3]
矩陣2
 a[1][1]  a[1][2]  a[1][3]
 a[2][1]  a[2][2]  a[2][3]
 a[3][1]  a[3][2]  a[3][3]
矩陣1*矩陣2
dp[2][1] dp[2][2] dp[2][3]

更大的數據以此類推,因此很容易想到用矩陣快速冪優化。

而要求dp[k][],就要由dp[1][]乘k-1次矩陣2,可以改為算出來矩陣2的k-1次冪放入矩陣3,再將dp[1][]乘上矩陣3,得到的就是dp[k][]。最終答案就是dp[k][1]+..+dp[k][n]。

所以說...這個矩陣快速冪的題..居然不用自己去構造轉移矩陣??

另外:

__builtin_popcountll:參照__builtin_popcount,那個是針對long整型的,這個是針對long long的

還有手動寫的

技術分享

 1 #include<cstdio>
 2 #include<cstring>
 3
#define md 1000000007 4 typedef long long LL; 5 LL n,k,anss; 6 LL a[101]; 7 struct Mat 8 { 9 LL data[101][101],x,y; 10 Mat() 11 { 12 memset(data,0,sizeof(data)); 13 x=y=0; 14 } 15 Mat operator*(const Mat& b) 16 { 17 Mat temp; 18 LL i,j,k; 19 for(i=1;i<=x;i++) 20 for(j=1;j<=b.y;j++) 21 for(k=1;k<=y;k++) 22 temp.data[i][j]=(data[i][k]*b.data[k][j]+temp.data[i][j])%md; 23 temp.x=x; 24 temp.y=b.y; 25 return temp; 26 } 27 Mat& operator*=(const Mat& b) 28 { 29 return (*this)=(*this)*b; 30 } 31 Mat& operator=(const Mat& b) 32 { 33 memcpy(data,b.data,sizeof(data)); 34 x=b.x; 35 y=b.y; 36 return *this; 37 } 38 }ma,o,bbb,ccc; 39 Mat pow(const Mat& a,LL b) 40 { 41 Mat ans=o; 42 if(b==0) return ans; 43 Mat base=a; 44 while(b!=0) 45 { 46 if(b&1!=0) ans*=base; 47 base*=base; 48 b>>=1; 49 } 50 return ans; 51 } 52 int main() 53 { 54 LL i,j; 55 scanf("%I64d%I64d",&n,&k); 56 for(i=1;i<=n;i++) 57 scanf("%I64d",&a[i]); 58 ma.x=ma.y=n; 59 for(i=1;i<=n;i++) 60 for(j=1;j<=n;j++) 61 ma.data[i][j]=(__builtin_popcountll(a[i]^a[j])%3==0); 62 o.x=o.y=n; 63 for(i=1;i<=n;i++) 64 for(j=1;j<=n;j++) 65 o.data[i][j]=(i==j); 66 bbb=pow(ma,k-1); 67 ccc.x=1;ccc.y=n; 68 for(i=1;i<=n;i++) 69 ccc.data[1][i]=1; 70 ccc*=bbb; 71 for(i=1;i<=n;i++) 72 anss=(anss+ccc.data[1][i])%md; 73 printf("%I64d",anss); 74 return 0; 75 }

Xor-sequences CodeForces - 691E || 矩陣快速冪