【题解】
题意:输出删除不同边使得所有连通图上无环的所有可能情况并取模,每条边最多只存在于一个环中。
思路:令环的个数为N,每个环内的边数为ai,总边数为m,则答案为 ,即累乘2^(每个环内的边数)-1再乘上2^(无影响的边的数目)。
这个答案很好得到,但是我们要怎么实现计算出每个环内的边数呢?
我们考虑到每条边最多只存在于一个环中,所以只要我们把一个环中的任意一条边删除并且记录下来,我们就会得到一棵树,那么我们就可以把问题转化成 ---> 求给定一对顶点的树上最短路径问题。那么我们怎么确定这对顶点呢?我们用并查集判断是否成环即可找出这条边,即连接这一对点的边。
【代码】
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
const ll mod=998244353;
int pre[maxn],l[maxn],r[maxn];
int vis[maxn];
struct Edge{
int to,next;
}edge[maxn*2];
int Find(int x){
return pre[x]==x?x:pre[x]=Find(pre[x]);
}
int head[maxn],tot;
void addedge(int u,int v){
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
int fa[maxn][20];
int deg[maxn];
void bfs(int root){
queue <int> que;
deg[root]=0;
fa[root][0]=root;
que.push(root);
while(!que.empty()){
int tmp=que.front();
que.pop();
for(int i=1;i<20;i++)
fa[tmp][i]=fa[fa[tmp][i-1]][i-1];
for(int i=head[tmp];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==fa[tmp][0]) continue;
deg[v]=deg[tmp]+1;
fa[v][0]=tmp;
que.push(v);
}
}
}
int LCA(int u,int v){
if(deg[u]>deg[v]) swap(u,v);
int hu=deg[u],hv=deg[v];
int tu=u,tv=v;
for(int det=hv-hu,i=0;det;det>>=1,i++)
if(det&1)
tv=fa[tv][i];
if(tu==tv) return tu;
for(int i=19;i>=0;i--){
if(fa[tu][i]==fa[tv][i])
continue;
tu=fa[tu][i];
tv=fa[tv][i];
}
return fa[tu][0];
}
ll qpow(ll a,ll b)
{
ll ret=1;
while(b){
if(b&1) ret=ret*a%mod;
a=a*a%mod;
b>>=1;
}
return ret;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m)){
tot=0;
for(int i=1;i<=n;i++)
pre[i]=i,head[i]=-1;
int cnt=0;
for(int i=0;i<m;i++){
int u,v; scanf("%d%d",&u,&v);
if(Find(u)==Find(v)) l[cnt]=u,r[cnt++]=v;
else{
pre[Find(u)]=Find(v);
addedge(u,v),addedge(v,u);
}
}
for(int i=1;i<=n;i++)
if(Find(i)==i)
bfs(i);
ll ans=1;
for(int i=0;i<cnt;i++){
int num=LCA(l[i],r[i]);
int sum=deg[l[i]]+deg[r[i]]-deg[num]*2+1;
ans=ans*(qpow(2,sum)-1)%mod;
m-=sum;
}
ans=ans*qpow(2,m)%mod;
printf("%lld\n",ans);
}
}