yww 与连通块计数

时间:2020-04-21
本文章向大家介绍yww 与连通块计数,主要包括yww 与连通块计数使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

yww 与连通块计数

分析

观察题目的额外限制,\(s1 | a_i ,a_i| s2\)\(\nexists i>1 ,i^2|s2\)

这意味着对于\(s2\)的每个因数,出现次数为\(1\)

如果把\(a_i,s1,s2\)全部除去\(gcd(s1,s2)\),那么按照剩下所有因数给\(a_i\)标记一个二进制状态\(C_i\)

那么在选择的联通块里,已经满足条件的因数集合所有的\(\{C_u \ xor \ C_v|(u,v)\in E,u \in V',v\in V'\}\)的或

因为如果出现\(C_u\)\(C_v\)的某一位不同\(\Leftrightarrow\)就意味着这个因数已经出现了\(0,1\)两种情况,即同时满足了\(\text{GCD}\)\(\text{LCM}\)的限制

这时候我们有两种办法计算(个人推荐Solution2哈)

\(n\)的质因子集合为\(p\)

Solution1:FWT

对于点直接进行\(dp\)转移\(dp[u][S]\)表示当前最高点是\(u\),满足条件的因数集合是\(S\)的方案数

\(dp[u][i|j]=dp[u][i]\cdot dp[v][j]\)

由于有或的操作,所以可以用高位前缀和/\(FWT\)或卷积

在没有优化的情况下,两种做法复杂度均为\(O(n|p|2^{|p|})\)

但是不用每次都\(\text{FWT}\)过去\(\text{FWT}\)回来,所以省去之后复杂度为\(O(n\cdot 2^{|p|})\)

代码不是我的哈(@神仙Rtx)

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i(a), i##_END(b); i <= i##_END; i++)
#define drep(i, a, b) for(int i(a), i##_END(b); i >= i##_END; i--)
using namespace std;
const int maxn = 1010, mod = 1e9+7;
typedef long long ll;
vector<int> e[maxn];
int n, pr[60], cnt, v[60], hs[maxn];
ll a[maxn], s1, s2, f[maxn][(1<<15)+10], ans[(1<<15)+10];
inline void ins(ll &x, ll y) { x = (x + y >= mod ? x + y - mod : x + y); }

void init(int N) {
	rep(i, 2, N) {
		if(!v[i]) pr[++cnt] = i;
		for(int j = 1; j <= cnt && i * pr[j] <= N; ++j) 
			v[i * pr[j]] = 1;
	}
}
void rfwt(ll *p, int N) {
	for(int mid = 1; mid < N; mid <<= 1) 
		for(int i = 0; i < N; i += (mid << 1))
			for(int j = 0; j < mid; ++j) ins(p[i + j + mid], mod - p[i + j]);
}
void dfs(int x, int pa) {
	rep(i, 0, (1<<cnt)-1) f[x][i] = 1;
	rep(i, 0, e[x].size() - 1) if(e[x][i] != pa) {
		int y = e[x][i];
		dfs(y, x);
		int diff = hs[x] ^ hs[y];
		for(int s = diff; s < (1<<cnt); s = (s+1)|diff) 
			ins(f[x][s], f[x][s] * f[y][s] % mod);
	}
	rep(i, 0, (1<<cnt)-1) ins(ans[i], f[x][i]);
}
int main() {
	scanf("%d%lld%lld", &n, &s1, &s2), s2 /= s1;
	int x, y;
	rep(i, 1, n) scanf("%lld", &a[i]), a[i] /= s1;
	rep(i, 2, n) scanf("%d%d", &x, &y), e[x].push_back(y), e[y].push_back(x);
	init(50);
	rep(i, 1, n) rep(j, 1, cnt) if(a[i] % pr[j] == 0) hs[i] |= (1<<(j-1));
	dfs(1, 0);
	rfwt(ans, 1<<cnt); // 最后FWT回来就可以了
	int mx = 0;
	rep(i, 1, cnt) if(s2 % pr[i] == 0) mx |= (1<<(i-1));
	printf("%lld\n", ans[mx]);
	return 0;
}

Solution2:容斥

枚举不满足条件的因数集合为\(S\),那么必然有\((C_u \ xor \ C_v) \and S={\empty}\)

\(O(n) \ \ \text{dp}\)求解即可和上面一样,但是省去了第二维

然后就是简单的奇偶容斥了

可以看到,复杂度就是\(O(n\cdot 2^{|p|})\),跑满的

const int N=1e3+10;

int n,C[N],fac[N],fc;
ll A,B,g;
ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b); }
vector <int> G[N];
int S;
ll dp[N];

void dfs(int u,int f) {
	dp[u]=1;
	for(int v:G[u]) if(v!=f) {
		dfs(v,u);
		if(((C[u]^C[v])&S)==0) dp[u]=(dp[u]+dp[u]*dp[v])%P; // 按照限制dp
	}
}

int main(){
	n=rd(),A=rd<ll>(),B=rd<ll>(),g=gcd(A,B);
	A/=g,B/=g;
	rep(i,2,50) if(B%i==0) B/=i,fac[fc++]=i; // 这些是预处理因数集合
	rep(i,1,n) {
		ll x=rd<ll>()/g;
		rep(j,0,fc-1) if(x%fac[j]==0) C[i]|=1<<j;
	}
	rep(i,2,n) {
		int u=rd(),v=rd();
		G[u].pb(v),G[v].pb(u);
	}
	ll ans=0;
	for(S=0;S<(1<<fc);++S) { // 枚举不合法的状态
		int cnt=0;
		rep(i,0,fc-1) if(S&(1<<i)) cnt^=1;
		dfs(1,0);
		rep(i,1,n) if(cnt) ans-=dp[i];
				else ans+=dp[i]; // 偶加奇减
	}
	ans=(ans%P+P)%P;
	printf("%lld\n",ans);
}

原文地址:https://www.cnblogs.com/chasedeath/p/12743738.html