码迷,mamicode.com
首页 > 其他好文 > 详细

【组合数+Lucas定理】2017多校训练七 HDU 6129 Just do it

时间:2017-08-16 20:15:59      阅读:157      评论:0      收藏:0      [点我收藏+]

标签:name   组合数   异或   play   span   std   ace   lucas定理   open   

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

标签:name   组合数   异或   play   span   std   ace   lucas定理   open   

原文地址:http://www.cnblogs.com/itcsl/p/7375198.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!