Task200204.2 沙漠绿化

Task200204.2 沙漠绿化

题目描述:

定义沙漠是满足如下性质的图: 没有重边和自环,且图中任意一条边至多属于一个简单环。 给定一片沙漠,从中删去任意多条边,使得删去边之后的图是一个森林,求有多少种方法,答案对 998244353 取模。

这里必须吐槽一下…

题目描述很硬核:沙漠里有很多的仙人掌,把仙人掌修剪成树,就绿化了沙漠(笑)

看到这里就已经懵了?没关系,你只是不知道「仙人掌图」,不妨认识一下?

思路:

树中不能有环,所以要将「沙漠」删边变成「森林」,就是将每一个环至少删掉一条边,而不在环中的边则删不删都可以(森林中的树不要求连通)

重点在于环的查找:我们可以DFS记录边的深度,一旦从较深DFS到较浅,就说明找到了一个环

DFS的过程中我们有以下几种情况需要处理:

  1. DFS到了自己的父亲——continue;
  2. DFS到比自己浅的点——找到了一个环,环中的边数即为两点深度差+1;
  3. DFS到比自己深的店——访问到了已经统计过的环,不用管;
  4. DFS到未标记深度的点——一个新的点,将此点的深度标记为父亲的深度+1,并从此点开始DFS;

值得注意的一点在于情况4的处理,当我们遇到一个新的点并标记他的深度之后,我们需要从此点开始DFS,因为题目中并没有规定「沙漠」是一个连通的图(只有一株仙人掌的沙漠还叫沙漠?)

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
#define ll long long
using namespace std;
const int maxn=300010;
const ll mod=998244353;
int vis[maxn];
vector<int>map[maxn];
int n,m;
int ans,num;
ll qp(ll a,ll b){
    ll res=1;
    while(b>0){
        if(b%2)
            res=(res*a)%mod;
        a=(a*a)%mod;
        b/=2;
    }
    return res;
}
void dfs(int u,int fa){
    vis[u]=vis[fa]+1;
    for(int i=0;i<map[u].size();i++){
        int v=map[u][i];
        if(fa==v) continue;
        if(vis[v]==0)
            dfs(v,u);
        else if(vis[u]>vis[v]){
            int temp=vis[u]-vis[v]+1;
            num+=temp;
            ans=(ans%mod*(qp(2,temp)-1)%mod)%mod;
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++) map[i].clear();
    ans=1,num=0;
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        map[x].push_back(y);
        map[y].push_back(x);
    }
    for(int i=1;i<=n;i++){
        if(!vis[i])
            dfs(i,0);
    }
    ans=(ans%mod*(qp(2,m-num))%mod)%mod;
    cout<<ans<<endl;
    return 0;
}
Sorry potatoler, but I cannot recognize you.
Built with Hugo
Theme Stack designed by Jimmy