zoukankan      html  css  js  c++  java
  • yww 与连通块计数

    yww 与连通块计数

    分析

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

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

    如果把\(a_i,s1,s2\)全部除去\(s1\),那么按照剩下所有因数给\(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}\)的限制

    因为如果同时出现0,1,必然存在两个相邻点不同

    利用这个性质,我们只用考虑联通块相邻两个点不同的状态

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

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

    Solution1:FWT

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

    \(dp[u][i \or j \or (C_u \ xor \ C_v)]=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; // 意思是f[x][0]=1,手动FWT了
    	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); // 枚举diff的父集转移
    	}
    	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>(),B/=A;
    	rep(i,2,50) if(B%i==0) B/=i,fac[fc++]=i; // 这些是预处理因数集合
    	rep(i,1,n) {
    		ll x=rd<ll>()/A;
    		rep(j,0,fc-1) if(x%fac[j]==0) C[i]|=1<<j;
    	}
            A=1;
    	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);
    }
    
  • 相关阅读:
    金蝶k3 显示BOS序时簿并返回选中的值
    金蝶K3bos插件操作另一张单据
    H2.64的远程回放--开篇
    监控外网访问的几种方式
    ilbc编解码
    windows系统上安装与使用Android NDK r5 (转)
    安卓与PC网络对接实现视频实时播放
    WDR7500 花生壳问题
    要确保任何一次员工的晋升都符合公司的利益
    关于“部门建设”
  • 原文地址:https://www.cnblogs.com/chasedeath/p/12743738.html
Copyright © 2011-2022 走看看