2019ccpc秦皇岛1006 Forest Program

题目

  Z 国近年来一直在考虑遏制国土沙漠化的方案。在 Z 国广阔的疆域上,有着许多的沙漠。沙漠上干旱少雨,荒无人烟,仅有仙人掌能在这种魔鬼环境中生存。经过 Z 国地质探测局的调查,他们得到了沙漠的实地情况。Z 国的地质探测局是一个热爱 CCPC 的机构,他们喜欢使用图论的方式来描述看到的景色。在得到的数据中,沙漠中的每一个连通块都是一棵仙人掌;一个连通块是一棵仙人掌当且仅当连通块中不存在重边和自环,并且每一条边仅被至多一个简单环覆盖。

  经过一番评估,Z 国决定通过删去沙漠中的一些边,最终将沙漠变为森林。这里我们定义森林满足:森林中每一个连通块都是一棵树,而树是边数等于点数减一的连通块。现在给定一个包含 n 个点的沙漠,请你求出 Z 国一共有多少种满足要求的沙漠改造方案。两种方案不同当且仅当方案中被删去的边集不同。由于答案可能很大,请将最终答案对 998244353 取模后输出。
样例:
input:
  3 3
  1 2
  2 3
  1 3
output:
  7
input:
  3 3
  1 2
  2 3
  1 3
output:
  6 6
  1 2
  2 3
  3 1
  2 4
  4 5
  5 2
output:
  49

题意

  在一个无向图中(可能有多个连通块),保证无重边和自环,每一条边最多存在一个简单环中。求:有多少种方案把这个图变成森林(每一个连通点的数量等于边的数量+1)。

思路

  对于每一个简单环,假设其边数为m,则方案数为2m-1,若有多个简单环需要用乘法原理,所以最终只要用点双求出简单环即可,但是还要考虑单边的问题(两点一线),如果出现单边需要对结果乘2(两种情况,去掉这条边和不去掉这条边)。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 300005
#define maxm 1000005
#define INF 2147483647123456789
#define mod  998244353
#define IOS ios::sync_with_stdio(false)

int n,m;
struct Edge
{
    int st, en;
    Edge() {}
    Edge(ll a, ll b)
    {
        st = a, en = b;
    }
};

struct node
{
    int to,next;
} G[maxm];
int head[maxn],num;
void add(ll from,ll to)
{
    G[++num].next=head[from];
    G[num].to=to;
    head[from]=num;
}

stack <Edge> palm;          //保存路径
vector <Edge> block[maxn];  //同一个点双的边保存在一起
int dfn[maxn], low[maxn];
int ind, T;					//ind为点双的数量,T为时间
void tarjan(int u, int pre)
{
    dfn[u] = low[u] = ++T;
    for (int i = head[u]; i != -1; i = G[i].next)
    {
        int v = G[i].to;
        if (!dfn[v])
        {
            palm.push(Edge(u, v));
            tarjan(v, u);
            if (low[u]>low[v]) low[u] = low[v];
            if (dfn[u] <= low[v])
            {
                for (Edge temp; !palm.empty(); )
                {
                    temp = palm.top();
                    if (dfn[temp.st]<dfn[v]) break;
                    block[ind].push_back(temp), palm.pop();
                }
                block[ind++].push_back(Edge(u, v));
                palm.pop();
            }
        }
        else if (v != pre && dfn[v]<dfn[u])
        {
            palm.push(Edge(u, v));
            if (low[u]>dfn[v]) low[u] = dfn[v];
        }
    }
}

ll QuickPow(ll x,ll N)
{
    ll res = x;
    ll ans = 1;
    while(N)
    {
        if(N&1)
        {
            ans = ans * res%mod;
        }
        res = res*res%mod;
        N = N>>1;
    }
    return ans%mod;
}

void init()
{
    memset(head,-1,sizeof(head));
    num=0;
    ind=0,T=0;
}

int main()
{
    IOS;
    cin>>n>>m;
    init();
    for(int i=1; i<=m; i++)
    {
        int x,y;
        cin>>x>>y;
        add(x,y);
        add(y,x);
    }

    for(int i=1; i<=n; i++)
        if(!dfn[i])
            tarjan(i,i);

    ll ans=1;
    for(ll i=0; i<ind; i++)
    {
        //cout<<"b "<<block[i].size()<<"\n";
        if(block[i].size()>1)
        {
            ll t=block[i].size();
            t=QuickPow(2,t)-1;
            t=(t+mod)%mod;
            ans*=t;
            ans%=mod;
        }
        else if(block[i].size()==1)
        {
            ans*=2;
            ans%=mod;
        }
    }
    cout<<ans<<"\n";
    return 0;
}

发布了41 篇原创文章 · 获赞 2 · 访问量 1238

猜你喜欢

转载自blog.csdn.net/qq_41418281/article/details/101614499