Problem A 

给出一个$n$个点$m$条边的仙人掌图(每条边最多处于一个简单环中)。

使用$c$种颜色对这张图中的顶点染色,使得每一条无向边连接的两个点颜色不同。

求染色的方案数,$mod \ 998244353$的值。

对于$100\%$的数据满足,$ 1 \leq n,m \leq 10^6$

Solution : 

  对于一棵树的答案非常简单就是$c \times (c-1) ^ {n-1}$

  对于一个大环,我们不妨计算这个环上的方案数。

  设$f(n)$表示含有$n$个点的环上使用$c$种颜色染色的方案数,

  非常显然,$f(1) = c$ , $f(2) = c(c-1)$.

  若$c \geq 3$ 那么考虑对于所有节点不能和前一个刚被染色的节点重复染色,方案数就是$c \times (c-1)^{n-1}$

  然后考虑,最后一个点和第一个点的颜色不能重复,若重复,我们可以将其看做一个点,那么就变成了一个子问题。

  最终,$f(n) = c \times (c-1)^{n-1} - f(n-1)$

  对于仙人掌图我们考虑v-DCC缩点(即找点双联通分量)  

  对于一个点,可能在多个简单环里。

  那么对于这个点,只要被一个环计算过,那么在计算剩余环的时候应当认为这个点的颜色既定。

  所以,若多于一个点在$k$个简单环里,我们只需要将总方案数直接除以$c^{k-1}$即可,这是由于该点在其他$k-1$个环中被认为可以被染$c$种颜色,然而事实上,这个点的颜色在第一个环包含它的时候已经被计算。

  所以,本题只需要进行一遍tarjan的v-DCC缩点即可。

  复杂度$O(n)$

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=1e6+10,mo=998244353;
int dfn[N],low[N],n,m,tot,head[N],cnt,blo[N],f[N],c;
vector<int>dcc[N];
stack<int>s;
struct rec{
    int pre,to;
}a[N<<2];
void adde(int u,int v) {
    a[++tot].pre=head[u];
    a[tot].to=v;
    head[u]=tot;
}
int Pow(int x,int n) {
    int ans = 1;
    while (n) {
        if (n&1) ans=ans*x%mo;
        x=x*x%mo;
        n>>=1;
    }
    return ans%mo;
}
void tarjan(int u) {
    dfn[u] = low[u] = ++dfn[0]; s.push(u);
    for (int i=head[u];i;i=a[i].pre){
        int v=a[i].to; 
        if (!dfn[v]) {
            tarjan(v); low[u]=min(low[u],low[v]);
            if (low[v]>=dfn[u]) {
                int z; ++cnt;
                do {
                    z = s.top(); s.pop();
                    dcc[cnt].push_back(z);
                    blo[z]++;
                }while (v!=z);
                dcc[cnt].push_back(u);
                blo[u]++;
            }
        } else low[u] = min(low[u],dfn[v]);
    }
}
 main()
{
    scanf("%lld%lld%lld",&n,&m,&c);
    for (int i=1;i<=m;i++) {
        int u,v; scanf("%lld%lld",&u,&v);
        adde(u,v); adde(v,u);
    }
    f[1] = c; f[2] = c*(c-1) % mo;
    for (int i=3;i<=n;i++) 
        f[i]=((c*Pow(c-1,i-1)%mo-f[i-1])%mo+mo)%mo;
    tarjan(1);
    int ans = 1,ret = 0;
    for (int i=1;i<=n;i++) ret+=blo[i]-1;
    for (int i=1;i<=cnt;i++)
        ans = ans*f[dcc[i].size()]%mo;
    int ni = Pow(c,mo-2);   
    ans = ans * Pow(ni,ret) % mo;
    printf("%lld\n",ans);
    return 0;
}
A.cpp

相关文章:

  • 2021-11-03
  • 2021-09-07
  • 2021-07-08
  • 2022-02-14
  • 2021-12-05
  • 2022-01-02
  • 2022-01-02
  • 2021-07-28
猜你喜欢
  • 2021-12-14
  • 2022-12-23
  • 2022-12-23
  • 2021-11-18
  • 2021-06-13
  • 2021-10-19
  • 2021-12-03
相关资源
相似解决方案