HDU 5514 巧妙的容斥
阿新 • • 發佈:2018-11-05
題意:給你一些數,對於每一個數a,可以得到t=(t+a)%m,t可以無限制算下去,問0~m-1之間能被得到的數的和。
思路:根據歐幾里得原理,a能得到的數就是a和m的最大公約數在0~(m-1)的倍數。
所以容斥就可以算出答案,每個數的倍數等差求和。但是資料是1e9,要進行容斥的數非常多,
能確定的是最大公約數一定是m的因子x,x算一遍相當於x所有的倍數都算了一遍,
所以算某個因子的時候,只要看看這個因子的因子對這個數貢獻個多少次,次數*等差和就是
這個因子的答案。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+10; ll a[N], gg[N], p[N], tot; ll num[N]; ll n, m; ll vis[N]; int main(){ int T, cas=0; scanf("%d", &T); while(T--){ scanf("%lld%lld", &n, &m); memset(num, 0, sizeof num); memset(vis, 0, sizeof vis); for(int i=1; i<=n; i++){ scanf("%lld", &a[i]); gg[i]=__gcd(a[i], (ll)m); } sort(gg+1, gg+1+n); int cnt=unique(gg+1, gg+1+n)-gg-1; int up=sqrt(m+0.5); tot=0; for(int i=1; i<=up; i++){ if(m%i==0){ p[++tot]=i; if(m/i>up) p[++tot]=m/i; } } sort(p+1, p+1+tot); for(int i=1; i<=cnt; i++){ int k=lower_bound(p+1, p+1+tot, gg[i])-p; vis[k]=1; } ll ans=0; for(int i=1; i<=tot; i++){ if(vis[i]!=num[i]){ ans+=(vis[i]-num[i])*((m-1)/p[i])*(p[i]+(m-1)/p[i]*p[i])/2; // cout<<p[i]<<" "<<vis[i]-num[i]<<endl; for(int j=i+1; j<=tot; j++) if(p[j]%p[i]==0){ num[j]+=vis[i]-num[i]; vis[j]=1; } } } printf("Case #%d: %lld\n", ++cas, ans); } return 0; }