abc470f 题解
诈骗题。
套路地,我们建
最终答案就是每个联通块的排列数相乘……吗?注意到
考虑什么时候能取到奇数的答案。发现实际上就是浪费一步,某一步交换两个相同的字符。虽然题目保证
注意到进行偶数次操作和奇数次操作的答案应是完全对称的,所以取偶数的答案直接除 2 即可。
多重集排列公式:
附赛时代码,赛后删掉了一些比较唐的注释。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 5e5 + 10;
const ll mod = 998244353;
int n, m;
string s;
int a[MAXN], b[MAXN];
vector<int> e[MAXN];
ll siz[MAXN], cnt[MAXN], fre[MAXN][26], tot;
bool vis[MAXN];
ll fact[MAXN], inv[MAXN];
bool is[MAXN];
ll qpow(ll x, ll y){
ll res = 1;
while(y){
if(y & 1) res = (res * x) % mod;
x = (x * x) % mod, y >>= 1;
}
return res;
}
void bfs(int st){
queue<int> q; q.push(st);
++tot;
ll mx = 0;
while(q.size()){
int u = q.front(); q.pop();
if(vis[u]) continue;
vis[u] = true, siz[tot]++;
fre[tot][s[u] - 'a']++;
mx = max(mx, fre[tot][s[u] - 'a']);
for(auto &v : e[u])
q.push(v);
}
is[tot] = (mx > 1);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m >> s, s = " " + s;
fact[0] = 1;
for(int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i % mod;
inv[n] = qpow(fact[n], mod - 2);
for(int i = n; i >= 1; i--) inv[i - 1] = inv[i] * i % mod;
for(int i = 1; i <= m; i++){
cin >> a[i] >> b[i];
e[a[i]].push_back(b[i]), e[b[i]].push_back(a[i]);
}
for(int i = 1; i <= n; i++)
if(!vis[i]) bfs(i);
ll ans = 1;
bool hav = false;
for(int i = 1; i <= tot; i++){
ll res = fact[siz[i]];
for(int ch = 0; ch < 26; ch++)
if(fre[i][ch]) res = (res * inv[fre[i][ch]]) % mod;
hav |= is[i];
ans = (ans * res) % mod;
}
if(!hav) ans = (ans * inv[2]) % mod;
cout << ans;
return 0;
}