【(好题)组合数+Lucas定理+公式递推(lowbit+滚动数组)+打表找规律】2017多校训练七 HDU 6129 Just do it
http://acm.hdu.edu.cn/showproblem.php?pid=6129
【题意】
- 对于一个长度为n的序列a,我们可以计算b[i]=a1^a2^......^ai,这样得到序列b
- 重复这样的操作m次,每次都是从上次求出的序列a得到一个新序列b
- 给定初始的序列,求重复m次操作后得到的序列
【方法一】
假定n=5,我们模拟一次可以发现,经过m次操作后a1在b1......bn中出现的次数为:
- m=0: 1 0 0 0 0
- m=2: 1 2 3 4 5
- m=3: 1 3 6 10 15
- m=4:1 4 10 20 35
- m=5:1 5 15 35 70
(画个杨辉三角出来就可以看出这些都是组合数)
容易推出来操作m次后,a1在答案bi中出现的次数C(m+i-2,m-1),由于是异或,我们只需知道C(m+i-2,m-1)%2
根据Lucas定理(2是素数),x=m-1,y=m+i-2,C(x,y)%2为x和y写成二进制后每一位C(xi,yi)%2的连乘,可以发现,只有C(0,1)是0,也就是C(x,y)%2==0 <=> y的二进制位置为1的集合是x的子集 <=> (x&y)==y
这道题m=1的时候是n^2的,然而这个复杂度是可以过的(奇怪)
附上AC代码
【AC】
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=2e5+2; 5 ll a[maxn]; 6 ll b[maxn]; 7 int n; 8 ll m; 9 int main() 10 { 11 int T; 12 scanf("%d",&T); 13 while(T--) 14 { 15 memset(b,0,sizeof(b)); 16 scanf("%d%I64d",&n,&m); 17 for(int i=1;i<=n;i++) 18 { 19 scanf("%I64d",&a[i]); 20 } 21 for(int i=1;i<=n;i++) 22 { 23 ll y=m-1; 24 ll x=m+i-2; 25 if((x&y)==y) 26 { 27 for(int j=i;j<=n;j++) 28 { 29 b[j]^=a[j-i+1]; 30 } 31 } 32 } 33 for(int i=1;i<=n;i++) 34 { 35 if(i==1) printf("%I64d",b[i]); 36 else printf(" %I64d",b[i]); 37 } 38 puts(""); 39 } 40 return 0; 41 }
【经验】
- 把a,b二进制表达后b中1的位置是a中1的位置的子集 <=> (a&b)==b
- C(2^k-1,i)%2==1 <=> 0<=i<=2^k-1
********************************************************************************************************************************
【方法二】
dp(i, j)表示第i次变换第j列的数。
dp(i, j) = dp(i, j-1)^dp(i-1, j) = dp(i, j-2)^dp(i-1, j-1)^dp(i-1, j-1)^dp(i-2, j) = dp(i, j-2)^dp(i-2, j) => dp(i, j-2^n)^dp(i-2^n, j)
从m中的最高位或最低位开始递推。
类似于dp[i][j]=dp[i-1][j]+dp[i][j-1],最后求dp[m][j](1<=j<=n)我们可以开一个滚动数组从前往后递推(dp[i][j]=dp[i-1][j]+dp[i][j+1]则是从后往前推)
1 for(int i=1;i<=m;i++) 2 { 3 for(int j=1;j<=n;j++) 4 { 5 dp[j]^=dp[j-1]; 6 } 7 }
计算dp[i][j]=dp[i-2^n][j]^dp[i][j-2^n],用lowbit从右往左枚举m的每一位,每一次都对整列进行更新。
1 while(m) 2 { 3 int x=lowbit(m); 4 for(int j=x;j<=n;j++) 5 { 6 dp[j]^=dp[j-x]; 7 } 8 m-=x; 9 }
【AC】
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=2e5+3; 5 int n,m; 6 int dp[maxn]; 7 int main() 8 { 9 int T; 10 scanf("%d",&T); 11 while(T--) 12 { 13 scanf("%d%d",&n,&m); 14 for(int i=0;i<n;i++) 15 { 16 scanf("%d",&dp[i]); 17 } 18 while(m) 19 { 20 int x=m&(-m); 21 for(int i=x;i<n;i++) 22 { 23 dp[i]^=dp[i-x]; 24 } 25 m-=x; 26 } 27 for(int i=0;i<n;i++) 28 { 29 if(i==0) printf("%d",dp[i]); 30 else printf(" %d",dp[i]); 31 } 32 puts(""); 33 } 34 return 0; 35 }
***********************************************************************************************************************************
【方法三】
http://m.blog.csdn.net/silence255713/article/details/77200056