您的位置:首页 > 其它

「6月雅礼集训 2017 Day5」学外语

2017-06-22 14:22 260 查看
【题目大意】

给出$\{P_i\}$,求经过以下操作后能够得到的不同序列个数:

第一步,选择$i, j$,交换$P_i,P_j$;第二步,把所有$P_x=i$的$P_x$变为$j$,把所有$P_x=j$的$P_x$变为$i$。

$n \leq 10^5$

【题解】

显然就是求

给出一个基环内向森林,求交换编号后,不同构的个数。

考虑什么时候会发生同构的情况。

容易发现,每当出现k个子树相同,或k个环套树相同,就有 k! 种同构方案。

那么我们只需要做一遍树哈希、环套树哈希(可以用最小表示法,或者按照哈希值最小作为起点)来处理。

代码好难写。。。

还被卡哈希了。。还没调处来qwq

upd: 是我找循环节错了

最小表示法:

# include <map>
# include <math.h>
# include <vector>
# include <stdio.h>
# include <assert.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e5 + 10, N = 2e5 + 10;
const int mod = 1e9+7;

# define RG register
# define ST static

inline int getint() {
int x = 0; char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return x;
}

int n, p[M], ans, fuckyou = 1;
struct Graph {
int n, head
, nxt[M], to[M], tot;
inline void set(int _n) {
n = _n; tot = 0;
for (int i=1; i<=n; ++i) head[i] = 0;
}
inline void add(int u, int v) {
++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v;
}
inline void adde(int u, int v) {
add(u, v), add(v, u);
}
}G;

struct us {
int fa
, n;
inline void set(int _n) {
n = _n; for (int i=1; i<=n; ++i) fa[i] = i;
}
inline int getf(int x) {
return fa[x] == x ? x : fa[x] = getf(fa[x]);
}
inline void un(int fu, int fv) {
fa[fu] = fv;
}
}U;

vector<int> ring
; int rn = 0;

namespace FACINV {
int fac
, inv
, Inv
;

inline int pwr(int a, int b, int P = mod) {
int ret = 1;
while(b) {
if(b&1) ret = 1ll * ret * a % P;
a = 1ll * a * a % P;
b >>= 1;
}
return ret;
}

inline void prepare() {
fac[0] = 1; inv[0] = 1;
for (int i=1; i<=100000; ++i) fac[i] = 1ll * fac[i-1] * i % mod;
inv[100000] = pwr(fac[100000], mod-2);
for (int i=99999; i; --i) inv[i] = 1ll * inv[i+1] * (i+1) % mod;
for (int i=99999; i; --i) Inv[i+1] = 1ll * inv[i+1] * fac[i] % mod;
Inv[1] = 1;
}

inline int gP(int x, int y) {
assert(x >= y);
return 1ll * fac[x] * inv[x-y] % mod;
}

inline int gC(int x, int y) {
assert(x >= y);
return 1ll * fac[x] * inv[x-y] % mod * inv[y] % mod;
}
}

using namespace FACINV;

namespace SOLVE_RINGS {
bool inrings
;
int c
, cn = 0, deg
;
bool vis
;
inline void dfs_rings(int x) {
if(vis[x]) {
++rn; for (int i=cn; i; --i) ring[rn].push_back(c[i]);
return ;
}
c[++cn] = x; vis[x] = 1;
for (int i=G.head[x]; i; i=G.nxt[i]) ++deg[x], dfs_rings(G.to[i]);
--cn;
}

inline void find_rings() {
U.set(n);
for (int i=1; i<=n; ++i) {
RG int fu = U.getf(p[i]), fv = U.getf(i);
if(fu == fv) inrings[i] = 1;
else U.un(fu, fv);
}
for (int i=1; i<=n; ++i) {
if(!inrings[i]) continue;
cn = 0;
dfs_rings(i);
}
}

inline void debug_rings() {
for (int i=1; i<=rn; ++i) {
printf("num = %d\n  ", i);
for (int j=0; j<ring[i].size(); ++j)
cout << ring[i][j] << ' ';
cout << endl;
}
}

inline void clear_rings() {
for (int i=1; i<=rn; ++i) ring[i].clear();
for (int i=1; i<=n; ++i) inrings[i] = vis[i] = deg[i] = 0;
rn = 0;
}
}

using namespace SOLVE_RINGS;

struct pa {
ull hash; int sz;
pa() {}
pa(ull hash, int sz) : hash(hash), sz(sz) {}
};
ull HA[M], HB[M];

vector<pa> tem;
inline void debug_tem() {
for (int i=0; i<tem.size(); ++i)
printf("hash = %lld,  size = %d\n", tem[i].hash, tem[i].sz);
}

inline bool cmp_hash(pa a, pa b) {
return a.hash < b.hash;
}

inline void gs(bool flag) {
if(flag) sort(tem.begin(), tem.end(), cmp_hash);
for (int i=0, j, t; i<tem.size(); i=j) {
j = i; while(j<tem.size() && tem[j].hash == tem[i].hash) ++j;
fuckyou = 1ll * fuckyou * fac[j-i] % mod;
}
}

inline void gs2() {
int sz = tem.size();
for (int i=1; i*i<=sz; ++i) {
if(sz % i == 0) {
bool gg = 0;
int d = sz/i;
for (int j=0; j<sz; ++j) {
if(tem[j].hash != tem[(j-i+sz) % sz].hash) {
gg = 1;
break;
}
}
if(!gg) {
fuckyou = 1ll * fuckyou * d % mod;
return ;
}
}
}
for (int i=sqrt(sz); i; --i) {
if(sz % i == 0) {
bool gg = 0;
int d = i;
for (int j=0; j<sz; ++j) {
if(tem[j].hash != tem[(j-sz/i+sz) % sz].hash) {
gg = 1;
break;
}
}
if(!gg) {
fuckyou = 1ll * fuckyou * d % mod;
return ;
}
}
}
return ;
}

inline int mininum() {
ull mi = tem[0].hash; int id = 0;
for (int i=1; i<tem.size(); ++i)
if(tem[i].hash > mi) mi = tem[i].hash, id = i;
return id;
}

pa f
, g
;
int sze
;
inline void dfs_tree(int x, int fa = 0, int d = 0) {
sze[x] = 1;
for (int i=G.head[x]; i; i=G.nxt[i]) {
if(G.to[i] == fa) continue;
dfs_tree(G.to[i], x, d+1);
sze[x] += sze[G.to[i]];
}
tem.clear();
int sons = 0;
for (int i=G.head[x]; i; i=G.nxt[i]) {
if(G.to[i] == fa) continue;
tem.push_back(f[G.to[i]]); ++ sons;
}
sort(tem.begin(), tem.end(), cmp_hash);
ull hsh = 666623333ull;
for (int i=0; i<tem.size(); ++i) hsh = (hsh << 13) ^ ((hsh & tem[i].hash) << 7) + ((67 * hsh ^ tem[i].hash) >> 13) ^ (tem[i].hash << 5);
gs(0); hsh = hsh ^ HA[sze[x]] + HB[sons];
f[x] = pa(hsh, sze[x]);
}

inline pa deal(vector<int> r) {
int SZ = 0;
for (int i=0; i<r.size(); ++i) {
int nx = r[(i-1 + r.size()) % r.size()];
dfs_tree(r[i], nx);
SZ += sze[r[i]];
}
tem.clear();
for (int i=0; i<r.size(); ++i) tem.push_back(f[r[i]]);
//    debug_tem();
int fro = mininum();
ull hsh = 19260817ull;
for (int i=0; i<tem.size(); ++i) {
int j = (i + fro) % tem.size();
hsh = (hsh << 7) ^ ((hsh & tem[j].hash) >> 2) + (103 * hsh & tem[j].hash) ^ ((233 * tem[j].hash ^ hsh) << 19) ^ (tem[j].hash + 233);
}
hsh = hsh ^ HA[SZ];
gs2();
pa ret = pa(hsh, SZ);
return ret;
}

inline void sol() {
n = getint(); G.set(n); ans = fac
; fuckyou = 1;
for (int i=1; i<=n; ++i) {
p[i] = getint();
G.add(p[i], i);
}

find_rings();
//    debug_rings();

for (int i=1; i<=rn; ++i) g[i] = deal(ring[i]);

tem.clear();
for (int i=1; i<=rn; ++i) tem.push_back(g[i]);
gs(1);
ans = 1ll * ans * pwr(fuckyou, mod-2) % mod;
--ans; if(ans < 0) ans += mod;
cout << ans << endl;
clear_rings();
}

inline ull irand() {
ull t = 0;
for (int i=0; i<=6; ++i) t = (t<<15) + rand();
return t;
}

int main() {
freopen("langue.in", "r", stdin);
freopen("langue.out", "w", stdout);
prepare();
for (int i=1; i<=n+n; ++i) HA[i] = irand(), HB[i] = irand();
int T; T = getint();
while(T--) sol();
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: