“玲珑杯”线上赛 Round #17 河南专场 B:震惊,99%+的中国人都会算错的问题(容斥计算)

传送门

题意

分析

是一道稍微变形的容斥题目,容斥一般的公式

\[ans=\sum_iAi-\sum_{i<j}{Ai∩Aj}+\sum_{i<j<k}{Ai∩Aj∩Ak}+... \]

但是这道题只要奇数次数的,那么对于第k项乘以一个系数\(2^{k-1}\)
具体见代码

trick

如果在每次dfs中for循环前加一个判断:if(lcm<=n)...
时间会从1000ms降到20ms

代码

#include<cstdio>

#define ll long long
#define F(i,a,b) for(int i=a;i<=b;++i)
#define R(i,a,b) for(int i=a;i<b;++i)
#define mem(a,b) memset(a,b,sizeof(a))
//#pragma comment(linker, "/STACK:102400000,102400000")
//inline void read(int &x){x=0; char ch=getchar();while(ch<'0') ch=getchar();while(ch>='0'){x=x*10+ch-48; ch=getchar();}}
inline ll gcd(ll a,ll b) {return b==0?a:gcd(b,a%b);}
int t,n,m;
ll lcm,a[16],ans;
/*
举个栗子,如A,B,C,那么第一次dfs会计算A,AB,AC,ABC的值,第二次dfs会计算B,BC,第三次dfs会计算C
,希望大家能够理解
*/
void dfs(int cur,ll lcm,int num)//当前第cur个,需要乘的系数为2^(num-1)
{
	lcm=a[cur]/gcd(a[cur],lcm)*lcm;
	if(num&1) ans+=n/lcm*(1ll<<(num-1));
	else ans-=n/lcm*(1ll<<(num-1));
	if(lcm<=n) for(int i=cur+1;i<m;++i)  dfs(i,lcm,num+1);
}
int main()
{
    for(scanf("%d",&t);t--;)
    {
    	scanf("%d %d",&n,&m);
    	ans=0;
    	for(int i=0;i<m;++i) scanf("%lld",a+i);
    	for(int i=0;i<m;++i)  dfs(i,a[i],1);
    	printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2017-06-24 19:02  遗风忘语  阅读(383)  评论(0编辑  收藏  举报