• ARC062


    似乎好久都没写博客了....赶快来补一篇

    题意

    给你一个 (n) 个点 , 没有重边和自环的图 .

    (m) 条边 , 每条边可以染 (1 o k) 中的一种颜色 .

    对于任意一个简单环 , 可以将它的边的颜色进行旋转任意位 .

    询问本质不同的染色方案数个数 .

    数据范围

    (1le n le 50, 1 le m le 100,1 le k le 100\)

    题解

    将边 (或者说是很多条边) 分为 (3) 种类型 :

    1. 不属于任何一个简单环 , 它的贡献为 (k) .

    2. 属于且仅属于一个简单环 (除了环上的边没边了) , 设环长为 (n) . 它的贡献就是

    [displaystyle frac{1}{n} sum_{i=0}^{n-1} k^{gcd(i, n)} ]

    这个就是类似于项链染色的方案数求解 , 原因见 此篇博客 .

    1. 属于多个环 (或者说是构成了的环 , 除了环上的边还有其他边) . 能够证明可以通过旋转来交换任意两条边的颜色 .
      于是本质不同当且仅当有一种颜色数量不同 , 那计算的话 , 就是利用隔板法 把 (m) 条边 分成 (k) 组的方案数 (每组不一定要有边)

      那么我们肖就加入多的 (k - 1) 个隔板 , 然后贡献很显然就是$${n + k - 1 choose k - 1}$$

    这个全都可以利用 (Tarjan) 求点双联通分量 (求割点的方法) 来判断种类 , 并在其中计算 , 把所有贡献乘起来就是最后的答案了.

    时间复杂度就是 (O(n+m)) 轻松通过此题.

    代码

    #include <bits/stdc++.h>
    #define For(i, l, r) for(int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    using namespace std;
    
    inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
    
    inline int read() {
        int x = 0, fh = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1;
        for (; isdigit(ch); ch = getchar() ) x = (x << 1) + (x << 3) + (ch ^ '0');
        return x * fh;
    }
    
    
    void File() {
    #ifdef zjp_shadow
    	freopen ("F.in", "r", stdin);
    	freopen ("F.out", "w", stdout);
    #endif
    }
    
    const int N = 55, M = 205;
    
    typedef long long ll;
    const ll Mod = 1e9 + 7;
    
    ll fpm(ll x, int power) {
    	ll res = 1;
    	for (; power; power >>= 1, (x *= x) %= Mod)
    		if (power & 1) (res *= x) %= Mod;
    	return res;
    }
    
    ll fac[M], ifac[M];
    
    void Init(int maxn) {
    	fac[0] = ifac[0] = 1ll;
    	For (i, 1, maxn) fac[i] = fac[i - 1] * i % Mod;
    	ifac[maxn] = fpm(fac[maxn], Mod - 2);
    	Fordown (i, maxn - 1, 1) ifac[i] = ifac[i + 1] * (i + 1) % Mod;
    }
    
    ll C(int m, int n) {
    	if (m > n || n < 0 || m < 0) return 0ll;
    	return fac[n] * ifac[m] % Mod * ifac[n - m] % Mod;
    }
    
    set <int> Point;
    int n, m, k; ll ans = 1ll;
    
    ll Polya(int n) {
    	ll res = 0;
    	For (i, 1, n) (res += fpm(k, __gcd(n, i))) %= Mod;
    	return res * fpm(n, Mod - 2) % Mod;
    }
    
    ll Permu(int m) { return C(k - 1, m + k - 1); }
    
    vector<int> G[N];
    int dfn[N], lowlink[N], sta[N], top;
    void Tarjan(int u, int fa) {
    	static int clk = 0;
    	dfn[u] = lowlink[u] = (++ clk); sta[++ top] = u;
    
    	for (int v : G[u]) if (!dfn[v]) {
    		Tarjan(v, u), chkmin(lowlink[u], lowlink[v]);
    		if (lowlink[v] >= dfn[u]) {
    			Point.clear();
    			int n = 0, m = 0, Last;
    			do Point.insert(Last = sta[top --]), ++ n; while (Last != v);
    			Point.insert(u), ++ n;
    			for (int x : Point) for (int v : G[x])
    				if ((bool)Point.count(v)) ++ m;
    			m >>= 1;
    
    			if (m < n) (ans *= k) %= Mod;
    			if (m == n) (ans *= Polya(n)) %= Mod;
    			if (m > n) (ans *= Permu(m)) %= Mod;
    		}
    	} else chkmin(lowlink[u], dfn[v]);
    	if (!fa) -- top;
    }
    
    int main () {
    	File();
    	n = read(), m = read(); k = read();
    	Init(m + k + 5);
    
    	For (i, 1, m) {
    		int u = read(), v = read();
    		G[u].push_back(v);
    		G[v].push_back(u);
    	}
    
    	For (i, 1, n) if (!dfn[i]) Tarjan(i, 0);
    	printf ("%lld
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    Primary key and Unique index
    Hash unique和Sort unique
    Oracle索引扫描算法
    Oracle预估的基数算法
    PGA突破pga_aggregate_target限制
    aix ipcs使用说明
    开窗函数和聚合函数区别
    【39.66%】【codeforces 740C】Alyona and mex
    【81.82%】【codeforces 740B】Alyona and flowers
    Android SDK离线安装
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/8997612.html
Copyright © 2020-2023  润新知