#include<cstdio>
#include<cstring>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
register LL x=0,f=1;register char c=getchar();
while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
return f*x;
}
const int MAXN=1e5+5;
const int MAXM=2e5+25;
const int MAXX=15;
const int mod=998244353;
int f[MAXN][2],p[MAXN][2];bool b[MAXN][2];
int dfn[MAXN],size[MAXN],eu[MAXN],ev[MAXN];
bool mark[MAXN],vis[MAXN];
int n,m,dft,Xcnt,ans;
inline int add(int x,int y){x+=y;return x>mod?x-mod:x;}
inline int mul(LL x,int y){x*=y;return x>mod?x%mod:x;}
struct Node{
int x,y;
inline Node(){}
inline Node(int _x,int _y){x=_x,y=_y;}
inline friend Node operator + (Node a,Node b){return Node(add(a.x,b.x),add(a.y,b.y));}
inline friend Node operator * (Node a,int b){return Node(mul(a.x,b),mul(a.y,b));}
}k[MAXN][2];
struct Edge{int v,next;}e[MAXM];
int first[MAXN],Ecnt=0;
inline void Add_edge(int u,int v){
e[++Ecnt]=(Edge){v,first[u]};
first[u]=Ecnt;
}
struct EXedge{int v,next;Node a,b;}E[MAXM];
int First[MAXN],ECNT=0;
inline void ADD_EDGE(int u,int v,Node a,Node b){
E[++ECNT]=(EXedge){v,First[u],a,b};
First[u]=ECNT;
}
inline void dfs1(int u,int pre){
dfn[u]=++dft;
for(int i=first[u],v=e[i].v;i;v=e[i=e[i].next].v){
if(v==pre) continue;
if(!dfn[v]){
dfs1(v,u);
size[u]+=size[v];
}
/*else if(dfn[u]<dfn[v]){
eu[++Xcnt]=u,ev[Xcnt]=v;
mark[u]=mark[v]=1;
//printf("%d-%d\n",u,v);
}*/
else{
mark[u]=1;//mark[u]是必须即时更新的
if(dfn[u]<dfn[v]) eu[++Xcnt]=u,ev[Xcnt]=v;
}
}
mark[u]|=size[u]>=2;
size[u]=size[u]||mark[u];
}
inline int dfs2(int u){
vis[u]=true;
p[u][0]=p[u][1]=1;
int pos=0,w;
for(int i=first[u];i;i=e[i].next){
int v=e[i].v;
if(vis[v]) continue;//这样和dfs1配合起来可以保证对于非树边的操作正确,在本题中非树边就直接单独讨论了,和dfs2无关
w=dfs2(v);
if(!w){
p[u][1]=1ll*p[u][1]*p[v][0]%mod;//没有关键点:直接按普通的更新
p[u][0]=1ll*p[u][0]*(p[v][0]+p[v][1])%mod;
}
else if(mark[u]) ADD_EDGE(u,w,k[v][0]+k[v][1],k[v][0]);
else k[u][1]=k[v][0],k[u][0]=k[v][0]+k[v][1],pos=w;//k已经从下面转移过来了
//记得更新pos(最近的关键点)!!!
}
if(mark[u]) k[u][0]={1,0},k[u][1]={0,1},pos=u;//类似于初始化的操作
else k[u][0]=k[u][0]*p[u][0],k[u][1]=k[u][1]*p[u][1];//然后再乘自己这个点,定义为处理完这个点的子树的系数
return pos;
}
inline void dp(int u){
//f[u][0]=b[u][0]*p[u][0];//这样写也是错的
//f[u][1]=b[u][1]*p[u][1];
f[u][0]=(b[u][1]^1)*p[u][0];//强制选了另一边,这一边就不能选
f[u][1]=(b[u][0]^1)*p[u][1];
for(int i=First[u];i;i=E[i].next){
int v=E[i].v;
dp(v);
f[u][0]=mul(f[u][0],add(mul(E[i].a.x,f[v][0]),mul(E[i].a.y,f[v][1])));//用处理好了的系数直接相乘
f[u][1]=mul(f[u][1],add(mul(E[i].b.x,f[v][0]),mul(E[i].b.y,f[v][1])));
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int x=read(),y=read();
Add_edge(x,y);
Add_edge(y,x);
}
dfs1(1,0);mark[1]=1;//根节点作为很多对的lca,必须标记
dfs2(1);
for(int i=0;i<=((1<<Xcnt)-1);i++){
for(int j=1;j<=Xcnt;j++){
if(i&(1<<(j-1))) b[eu[j]][1]=b[ev[j]][0]=1;// (1,0) 或者 (0,_)
else b[eu[j]][0]=1;
}
dp(1);
ans=add(ans,add(f[1][0],f[1][1]));
for(int j=1;j<=Xcnt;j++){
if(i&(1<<(j-1))) b[eu[j]][1]=b[ev[j]][0]=0;
else b[eu[j]][0]=0;
}
}
printf("%d\n",ans);
}
转载于:https://www.cnblogs.com/lizehon/p/10567892.html