Xor-sequences CodeForces - 691E || 矩阵快速幂

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 }

 

posted @ 2017-09-30 16:24  hehe_54321  阅读(398)  评论(0编辑  收藏  举报
AmazingCounters.com