题目大意:
多组数据,每组给定n,m,表示将n个小球放进m个箱子,每个小球均有两个箱子(可能相同)可放,求所有小球均放好的方案mod998244353的总数。
思路:
算是我和题解思路肥肠相近的一道题可是还是惨遭爆零
考虑将箱子视为点,小球视为边,每一个小球的合法去向连一条无向边,则问题转化为使给定无向图的每一个点赋上一个与边的编号相同的值,使得每条边都有且只有一个相邻点的值与之相同。
对于不同连通块,ans直接相乘
对于点数小于边数的连通块(也即是自己思路中“环套环”的部分),无法分配,ans直接为0
对于边数等于点数的连通块,可以发现是一个环(基环树),此时除非环的长度为1(自己连自己),ans=1,否则ans=2(有顺时针和逆时针两种分配方式)
对于边数等于点数-1的连通块,可以发现是一棵树,ans=连通块中的点数
证明:对于仅有一个点的树(其实是个孤独的点),显然ans=1.
如果对上述的有k个节点的树(或者是个孤独的点)增加一个点并连一条边,显然可以给新边赋值为新点的权值,ans=k;
也可以给新边赋值给树上点的权值,此时染色方案被唯一确定,ans=1;
故ans=k+1,也为点数,得证。
直接DFS遍历所有连通块即可,一组数据的复杂度为O(n)。
代码:
#include <cstdio> #include <algorithm> #include <queue> #include <cstring> #define int long long using namespace std; const int mod=998244353; const int maxn=300005; struct node{ int to,next; bool vis; }e[maxn<<1]; int head[maxn<<1]; int cnt; inline void add(int u,int v) { e[++cnt].to=v; e[cnt].next=head[u]; head[u]=cnt; } int num_p,num_e; bool isselfcir; bool vis[maxn]; inline void dfs(int now) { vis[now]=1; num_p++; for (int i=head[now];i;i=e[i].next) { int to=e[i].to; if (!e[i].vis) e[i].vis=1,num_e++; if (to==now) isselfcir=1; if (vis[to]) continue; dfs(to); } } signed main() { int T; scanf("%lld",&T); while (T--) { memset(vis,0,sizeof(vis)); memset(head,0,sizeof(head)); memset(e,0,sizeof(e)); cnt=0; int n,m; scanf("%lld%lld",&m,&n); for (int i=1;i<=m;i++) { int x,y; scanf("%lld%lld",&x,&y); add(x,y),add(y,x); } int ans=1; for (int i=1;i<=n;i++) { if (!vis[i]) { isselfcir=0,num_p=0,num_e=0; dfs(i); num_e>>=1; if (num_e>num_p) { ans=0; break; } if (num_e==num_p) if (!isselfcir) ans*=2,ans%=mod; if (num_e==num_p-1) ans*=num_p,ans%=mod; } } printf("%lld\n",ans); } }