题意:
n个青蛙,m个点,编号为0到m-1
每个青蛙的跳跃的距离为xi,起点都是0,问:m个点中,所有可以到达的编号的和是多少
分析样例:
先选个简单的:
2 12 9 10(9):产生的所有数是:3,6,9
(10):产生的所有数是:2,4,6,8,10
发现了GCD:(9,12)=3,(10,12)=2
而且和是与GCD,m有关的一个式子
那么6多了,如何处理呢?容斥原理减掉
选个最麻烦的吧:
9 96 81 40 48 32 64 16 96 42 72(81,96)=3,(40,96)=8,(48,96)=48,(32,96)=32,(64,96)=32,(16,96)=16,(96,96)=96,(42,96)=6,(72,96)=24
可以看到,除了3和8之外,其他的GCD都是3或者8的倍数(都可以扔掉,因为产生的数都会是重复的)
那么,根据刚才说的容斥:我们只需要算上3的,算上8的,减去24的(重复了)
这样算来,我们得到了一个好的做法(错误的做法):
把所有的GCD列出来,排个序,然后如果有倍数情况的扔掉,然后用容斥原理来搞一发!
(当然会有个小的剪枝:如果最小的GCD是1,那么表示所有的值都会取得到,提前输出m*(m-1)/2即可)
容斥原理的经典写法:状态压缩
假设有tot个GCD,那么maxnum=(1<<tot)
状态值从0枚举到maxnum-1,然后如果状态值st的二进制表示位上有奇数个1,说明这个值要加;否则,这个值要减
代码如下:wrong answer
为什么这么写不对呢??因为,我们在容斥的时候,会发现,需要把某些GCD值乘起来,是不是会超过m呢?
如果是连续的多个素数,2,3,5,7,11,13,17,19,23,取最小的连续9个素数
得到Hack数据1:
9 223092870
2 3 5 7 11 13 17 19 23
可以计算得:现在的m刚好是这9个数相乘,不会越界
那么,我们怎么样构造一组可以越界的呢?很简单,这些素数两两相乘!得到Hack数据2:
10 223092870
6 10 14 22 26 34 38 46 15 21
这样,这些数都满足GCD的条件,也相互不是倍数和约数的关系,但是乘起来是越界的,我们的程序没有处理到这一点
于是:我们必须采取另外的容斥姿势:从小到大,来枚举m的所有可能因子(首先把因子打表打到数组里)
然后还是计算每个青蛙跳跃距离和m的GCD值,然后根据GCD值,来看看GCD可以产生出来哪些因子,给予标记
然后定义num数组
当num数组不为1时,说明当前的容斥不对(那么我们需要加加减减来抵消掉其他的运算)
但是在这样抵消的时候,当前枚举值的倍数也被相应的影响了这么多的次数
所以,这是用另一种思路写的容斥,代码如下:
#include<bits/stdc++.h>
using namespace std;
#define LL __int64
const int maxn=1e5+50;
int p[maxn],cnt;
int vis[maxn];
int num[maxn];
LL gcd(LL x,LL y){
return x%y==0?y:gcd(y,x%y);
}
int main(){
//freopen("input.txt","r",stdin);
int t,n,m,x;
scanf("%d",&t);
for(int Case=1;Case<=t;Case++){
memset(vis,0,sizeof(vis));
memset(num,0,sizeof(num));
cnt=0;
scanf("%d%d",&n,&m);
for(int i=1;i*i<=m;i++)
if (m%i==0){
p[cnt++]=i;
if (i*i!=m) p[cnt++]=m/i;
}
sort(p,p+cnt);
//for(int i=0;i<cnt;i++) printf("%d%c",p[i],i==cnt-1?'\n':' ');
for(int i=1;i<=n;i++){
scanf("%d",&x);
x=gcd(x,m);
for(int j=0;j<cnt;j++)
if (p[j]%x==0) vis[j]=1;
}
vis[cnt-1]=0;
LL ans=0;
for(int i=0;i<cnt;i++)
if (vis[i]!=num[i]){
LL temp=m/p[i];
ans+=temp*(temp-1)/2*p[i]*(vis[i]-num[i]);
int change=vis[i]-num[i];
for(int j=i;j<cnt;j++)
if (p[j]%p[i]==0)
num[j]+=change;
}
printf("Case #%d: %I64d\n",Case,ans);
}
return 0;
}