码迷,mamicode.com
首页 > 移动开发 > 详细

ARC062 - F. Painting Graphs with AtCoDeer (Polya+点双联通分量)

时间:2018-05-06 12:15:56      阅读:174      评论:0      收藏:0      [点我收藏+]

标签:char   就是   \n   类型   graphs   har   pac   getc   display   

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

  • 题意: 给你一个 \(n\) 个点 , 没有重边和自环的图 .
    \(m\) 条边 , 每条边可以染 \(1 \to k\) 中的一种颜色 .
    对于任意一个简单环 , 可以将它的边的颜色进行旋转任意位 .
    询问本质不同的染色方案数个数 .

  • 数据范围:
    \(1\le 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)}\]
    这个就是类似于项链染色的方案数求解 , 原因见 此篇博客 .

  3. 属于多个环 . 能够证明可以通过旋转来交换任意两条边的颜色 .
    于是本质不同当且仅当有一种颜色数量不同 ,
    其贡献就是利用隔板法 把 \(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;

bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
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\n", ans);
    return 0;
}

ARC062 - F. Painting Graphs with AtCoDeer (Polya+点双联通分量)

标签:char   就是   \n   类型   graphs   har   pac   getc   display   

原文地址:https://www.cnblogs.com/zjp-shadow/p/8997612.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!