(CDOJ1544 [“玲瓏杯” 線上賽Round #17 B] 當鹹魚也要按照基本法
原題地址:
http://acm.uestc.edu.cn/#/problem/show/1544
http://www.ifrog.cc/acm/problem/1138
題意:
zhu有N條鹹魚(標號從1到N),每條鹹魚都有一個鹹魚值Ki,初始時所有Ki都是0.
zhu有M個鹹數,對於每個鹹數x,他都會讓所有滿足標號是x倍數的鹹魚的鹹魚值異或上1.
zhu現在想知道經過了這M個鹹數的篩選之後,最終有多少條的鹹魚的鹹魚值是1?
數據範圍
1≤T≤1000,1≤N≤1e9,1≤M≤15,1≤鹹數≤2∗1e5
題解:
原題意即:
給定m 個數a1…am, 統計[1,n] 的整數中, 滿足a1…am 中有奇數個數整除它的數的個數。
N很大,而M才15,要從M下手,
易得到至少整除集合s的數的個數,考慮容斥。
當然這要求有奇數個數整除它,容斥係數似乎不大好確定。
根據要求得到關於容斥係數的式子:
然後
(longlong不開WA一天)
代碼:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
const int N=20;
int T,m,f[N],C[N][N],a[N];
int cnt[100005],lg[100005];
LL n,lcm[100005],ans=0;
LL gcd(LL x,LL y) {return y==0?x:gcd(y,x%y);}
int main()
{
scanf("%d",&T);
for(int i=0;i<=16;i++) lg[1<<i]=i;
for(int i=0;i<=16;i++) for(int j=0;j<=i;j++) if(j==0||i==j) C[i][j]=1;else C[i][j]=C[i-1][j-1]+C[i-1][j];
f[1]=1;
for(int i=2;i<=16;i++)
{
int s=0; for(int j=1;j<i;j++) s+=C[i][j]*f[j];
f[i]=i%2-s;
}
while(T--)
{
scanf("%lld%d",&n,&m);
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
int top=1<<m; ans=0;
for(int s=1;s<top;s++)
{
int x=(s&(-s)); int ss=s^x;
if(!ss) {cnt[s]=1; lcm[s]=1LL*a[lg[x]+1];}
else if(lcm[ss]==-1) {lcm[s]=-1; cnt[s]=cnt[ss]+1; continue;}
else {cnt[s]=cnt[ss]+1; lcm[s]=1LL*lcm[ss]*(1LL*a[lg[x]+1]/gcd(lcm[ss],1LL*a[lg[x]+1]));}
ans+=1LL*f[cnt[s]]*1LL*(1LL*n/lcm[s]);
if(lcm[s]>n) lcm[s]=-1;
}
printf("%lld\n",ans);
}
return 0;
}