zoukankan      html  css  js  c++  java
  • 题解 LOJ #3304. 「联合省选 2020 A」作业题

    题目链接

    首先先把那个 (gcd) 给反演掉:

    [=sum_{d=1}^Wvarphi(d)sum_{T}sum_{win T,dmid w}w ]

    后面的部分是个经典的矩阵树应用:将边权改为 (wx+1),在模 (x^2) 意义下做矩阵树,最后返回一次项系数。

    然后直接做就行了。注意需要剪枝:如果边数小于 (n-1) 就跳过。

    #include<cstdio>
    #include<algorithm>
    
    #define fi first
    #define se second
    #define mp(x, y) std::make_pair(x, y)
    
    typedef long long ll;
    typedef std::pair<ll, ll> poly;
    const ll mod = 998244353;
    const int maxn = 35;
    const int W = 152501;
    
    int n, m, edg[W + 5]; ll g[maxn][maxn];
    int phi[W + 5], cnt, prime[W + 5];
    bool nprime[W + 5];
    
    inline ll fsp(ll a, ll b, const ll &mod = mod, ll res = 1) {
    	for(a %= mod, b %= mod - 1; b; a = a * a % mod, b >>= 1)
    		if(b & 1) res = res * a % mod; return res;
    }
    
    inline poly operator+(const poly &a, const poly &b) {
    	return mp((a.fi + b.fi) % mod, (a.se + b.se) % mod);
    }
    
    inline poly operator-(const poly &a, const poly &b) {
    	return mp((a.fi - b.fi) % mod, (a.se - b.se) % mod);
    }
    
    inline poly operator*(const poly &a, const poly &b) {
    	return mp((a.fi * b.se + a.se * b.fi) % mod, a.se * b.se % mod);
    }
    
    inline poly operator/(const poly &a, const poly &b) {
    	ll inv = fsp(b.se, mod - 2);
    	return mp((a.fi * b.se - a.se * b.fi) % mod * inv % mod * inv % mod, a.se * inv % mod);
    }
    
    inline void pre(int N) {
    	phi[1] = 1;
    	for(int i = 2; i <= N; ++i) {
    		if(!nprime[i]) prime[++cnt] = i, phi[i] = i - 1;
    		for(int j = 1; j <= cnt && i * prime[j] <= N; ++j) {
    			nprime[i * prime[j]] = 1;
    			if(i % prime[j]) phi[i * prime[j]] = phi[i] * (prime[j] - 1);
    			else { phi[i * prime[j]] = phi[i] * prime[j]; break; }
    		}
    	}
    }
    
    namespace MatrixTree {
    	poly Mat[maxn][maxn];
    	
    	inline ll work() {
    		ll sig = 1;
    		for(int i = 1; i < n; ++i) {
    			if(!Mat[i][i].se) {
    				bool flag = 0;
    				for(int j = i + 1; j < n; ++j)
    					if(Mat[j][i].se) {
    						flag = 1; std::swap(Mat[i], Mat[j]);
    						sig = -sig; break;
    					}
    				if(!flag) return 0;
    			}
    			
    			for(int j = i + 1; j < n; ++j) {
    				poly inv = Mat[j][i] / Mat[i][i];
    				for(int k = 1; k < n; ++k)
    					Mat[j][k] = Mat[j][k] - Mat[i][k] * inv;
    			}
    		}
    		
    		poly res = mp(0, 1);
    		for(int i = 1; i < n; ++i)
    			res = res * Mat[i][i];
    		return res.fi * sig;
    	}
    }
    
    int main() {
    	scanf("%d%d", &n, &m), pre(W);
    	for(int i = 1, u, v, w; i <= m; ++i) {
    		scanf("%d%d%d", &u, &v, &w);
    		g[u][v] = g[v][u] = w, ++edg[w];
    	}
    	
    	for(int p = 1; p <= cnt; ++p)
    		for(int i = W / prime[p]; i; --i)
    				edg[i] += edg[i * prime[p]];
    	
    	ll ans = 0;
    	for(int d = 1; d <= W; ++d) {
    		if(edg[d] < n - 1) continue;
    		
    		for(int i = 1; i <= n; ++i)
    			for(int j = 1; j <= n; ++j)
    				MatrixTree::Mat[i][j] = mp(0, 0);
    		for(int i = 1; i <= n; ++i) {
    			for(int j = 1; j < i; ++j) {
    				if(!g[i][j] || g[i][j] % d) continue;
    				
    				MatrixTree::Mat[i][i] = MatrixTree::Mat[i][i] + mp(g[i][j], 1);
    				MatrixTree::Mat[j][j] = MatrixTree::Mat[j][j] + mp(g[i][j], 1);
    				MatrixTree::Mat[i][j] = MatrixTree::Mat[i][j] - mp(g[i][j], 1);
    				MatrixTree::Mat[j][i] = MatrixTree::Mat[j][i] - mp(g[i][j], 1);
    			}
    		}
    		
    		(ans += phi[d] * MatrixTree::work()) %= mod;
    	} printf("%lld", (ans + mod) % mod);
    }
    
  • 相关阅读:
    小程序自定义组件(3)子向父传参
    postgresql插件安装
    二进制减法的实现
    mysql锁表问题
    mysql查看修改参数
    众数问题-找出超过一半的数
    只出现一次的数
    元素最大间距离
    第一个缺失数字
    局部最小值位置
  • 原文地址:https://www.cnblogs.com/whx1003/p/14201307.html
Copyright © 2011-2022 走看看