do_while_true

一言(ヒトコト)

「题解」Codeforces 585E Present for Vitalik the Philatelist

在众多科技普及的今天,它如同一道板题。

我的做法是,直接把式子列出来,暴力展开艾弗森记号,然后嗯推。

\[\begin{aligned} &\sum_{S\subseteq U}\sum_{x\in \complement_U S}[\gcd(S)>1][\gcd(x,S)=1] \\ =&\sum_{S\subseteq U}\sum_{x\in \complement_U S}(1-[\gcd(S)=1])[\gcd(x,S)=1] \\ =&\sum_{S\subseteq U}\sum_{x\in \complement_U S}[\gcd(x,S)=1]-\sum_{S\subseteq U}\sum_{x\in \complement_U S}[\gcd(S)=1][\gcd(x,S)=1] \\ =&\sum_{S\subseteq U}[\gcd(S)=1]|S|-\sum_{S\subseteq U}[\gcd(S)=1](n-|S|) \\ =&\sum_{S\subseteq U}[\gcd(S)=1](2|S|-n) \end{aligned} \]

倒数第二步的 \(S\) 实际上是在枚举倒数第三步中的 \(S\cup \{x\}\).那么现在问题就是对 \(\gcd\)\(1\) 的子集 \(S\) 计数,其权值为 \(2|S|-n\).对桶作狄利克雷后缀和对每个 \(d\) 求出 \(d\) 的倍数的个数,然后根据这个对每个 \(d\) 计算出 \(d|\gcd\) 的子集的权值和,再作狄利克雷后缀差分,在 1 处的点值即为答案。时间复杂度是 \(\mathcal{O}(n+a\log\log a)\)

有趣的现象是,洛谷题解中同时出现了莫比乌斯反演,组合意义容斥,生成函数,还有和我类似的暴力展开艾弗森记号后再考虑组合意义,我姑且认为这几者是等价的。

#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<ctime>
#include<random>
#include<assert.h>
#define pb emplace_back
#define mp make_pair
#define fi first
#define se second
#define dbg(x) cerr<<"In Line "<< __LINE__<<" the "<<#x<<" = "<<x<<'\n';
#define dpi(x,y) cerr<<"In Line "<<__LINE__<<" the "<<#x<<" = "<<x<<" ; "<<"the "<<#y<<" = "<<y<<'\n';
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>pii;
typedef pair<ll,int>pli;
typedef pair<ll,ll>pll;
typedef pair<int,ll>pil;
typedef vector<int>vi;
typedef vector<ll>vll;
typedef vector<pii>vpii;
typedef vector<pil>vpil;
template<typename T>T cmax(T &x, T y){return x=x>y?x:y;}
template<typename T>T cmin(T &x, T y){return x=x<y?x:y;}
template<typename T>
T &read(T &r){
	r=0;bool w=0;char ch=getchar();
	while(ch<'0'||ch>'9')w=ch=='-'?1:0,ch=getchar();
	while(ch>='0'&&ch<='9')r=r*10+(ch^48),ch=getchar();
	return r=w?-r:r;
}
template<typename T1,typename... T2>
void read(T1 &x,T2& ...y){read(x);read(y...);}
const int mod=1000000007;
inline void cadd(int &x,int y){x=(x+y>=mod)?(x+y-mod):(x+y);}
inline void cdel(int &x,int y){x=(x-y<0)?(x-y+mod):(x-y);}
inline int add(int x,int y){return (x+y>=mod)?(x+y-mod):(x+y);}
inline int del(int x,int y){return (x-y<0)?(x-y+mod):(x-y);}
int gcd(int a,int b){
	return !b?a:gcd(b,a%b);
}
int bit(int x){
	return 1<<(x-1);
}
const int N=500010;
int n,ans,ct1,mx;
int f[10000010],fpow[N],vis[10000010];
vi pr;
void pre(){
	for(int i=2;i<=mx;i++){
		if(!vis[i])pr.pb(i);
		for(auto x:pr){
			if(i*x>mx)break;
			vis[i*x]=1;
			if(i%x==0)break;
		}
	}
}
signed main(){
	#ifdef do_while_true
//		assert(freopen("data.in","r",stdin));
//		assert(freopen("data.out","w",stdout));
	#endif
	read(n);
	fpow[0]=1;for(int i=1,x;i<=n;i++)f[read(x)]++,ct1+=x==1,fpow[i]=add(fpow[i-1],fpow[i-1]),cmax(mx,x);
	pre();
	for(auto x:pr)for(int i=mx/x;i;i--)cadd(f[i],f[i*x]);
	for(int i=1;i<=mx;i++)
		f[i]=add(del(2ll*f[i]*fpow[f[i]-1]%mod,1ll*n*fpow[f[i]]%mod),n);
	for(auto x:pr)for(int i=1;i<=mx/x;i++)cdel(f[i],f[i*x]);
	cout << del(f[1],ct1) << '\n';
    #ifdef do_while_true
		cerr<<'\n'<<"Time:"<<1.0*clock()/CLOCKS_PER_SEC*1000<<" ms"<<'\n';
	#endif
	return 0;
}
posted @ 2022-10-08 09:42  do_while_true  阅读(18)  评论(0编辑  收藏  举报