FrogsTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 4142 Accepted Submission(s): 1396 Problem Description There are m stones lying on a circle, and n frogs are jumping over them. Input There are multiple test cases (no more than 20 ), and the first line contains an integer t , Output For each test case, you should print first the identifier of the test case and then the sum of all occupied stones' identifiers. Sample Input 3 2 12 9 10 3 60 22 33 66 9 96 81 40 48 32 64 16 96 42 72 Sample Output Case #1: 42 Case #2: 1170 Case #3: 1872 Source 2015ACM/ICPC亚洲区沈阳站-重现赛(感谢东北大学) Recommend wange2014 | We have carefully selected several similar problems for you: 6437 6436 6435 6434 6433 |
#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ll long long
/*
题目大意:n个青蛙,每个青蛙在一个0~m-1的环上,
从0出发,每个都有步数,要求开所有能被青蛙踩到的环下标和。
可以知道x=gcd(x,m)(模拟一下就可以感受到)
一开始用最普通的容斥做,dfs容斥,
就是只保留最小的因子(不一定是质数),
然后利用这些来进行容斥,光荣超时/。。。。。
最后还是采用了贡献的思想,
一旦有个因子能被青蛙走到,其倍数的贡献都加一,
在后面遍历计算时,如果该枚举的因子贡献不为零,则计算它的贡献值(要乘上其vis值,可能为负),
然后对于它的倍数减去它的贡献,
这样配合筛法进行容斥(我感觉筛法就是数论中的Dp精髓)。
可以看到vis数组的值变化在-1,0和1区间内。
并且该算法对每个因子维护的vis数组其实就是它的重复度,
本质上,比如有个数x,被前面的因子筛了s次,那么判别到它时肯定要减去它的重复度,
因为被筛了肯定也被初始化为1,所以重复度为s-1,减去后,后面的倍数也被更新,
被更新为0,因为前面因子的出现导致后面的倍数无影响或影响被覆盖,符合题目精髓。。。
*/
const int maxn =2e4+5;
const int mod=1e9+7;
ll gcd(ll x,ll y){return y==0?x:gcd(y,x%y);}
int n,m,x;
int yinzi[maxn],tot=0;
int vis[maxn];
///容斥的过程
int main()
{
int t;scanf("%d",&t);
for(int ca=1;ca<=t;ca++)
{
scanf("%d%d",&n,&m);
tot=0;yinzi[tot++]=1;///注意筛选因子的时候要加上1
for(int i=2;i*i<=m;i++)
{
if(m%i==0)
{
if(i*i==m) yinzi[tot++]=i;
else
{
yinzi[tot++]=m/i;
yinzi[tot++]=i;
}
}
}
sort(yinzi,yinzi+tot);
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++)
{
scanf("%d",&x);
x=gcd(x,m);///青蛙的步伐
for(int j=0;j<tot;j++) if(yinzi[j]%x==0) vis[j]=1;///初始化所有步数的倍数都要有贡献
}
ll ans=0;
for(int i=0;i<tot;i++)
{
if(vis[i])
{
ll tp=m/yinzi[i];
ans+=1LL*yinzi[i]*(tp-1)*tp/2*vis[i];
for(int j=i+1;j<tot;j++)
{
if(yinzi[j]%yinzi[i]==0)
vis[j]-=vis[i];///计算过的就不用计算了,减去它的贡献,另一类容斥
}
}
}
printf("Case #%d: %lld\n",ca,ans);
}
return 0;
}