1. 程式人生 > >【組合數+Lucas定理】2017多校訓練七 HDU 6129 Just do it

【組合數+Lucas定理】2017多校訓練七 HDU 6129 Just do it

clu sca def opened == cnblogs long 合數 color

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 }
View Code

【經驗】

把a,b二進制表達後b中1的位置是a中1的位置的子集

<=> (a&b)==b

【組合數+Lucas定理】2017多校訓練七 HDU 6129 Just do it