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

【题目大意】

给出$\{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[N], 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], 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[N]; int rn = 0; 

namespace FACINV {
    int fac[N], inv[N], Inv[N];
    
    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[N];
    int c[N], cn = 0, deg[N]; 
    bool vis[N]; 
    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() {
    int i=0, j=1, len = tem.size(), l;
    while(i<len && j<len) {
        for (l=0; l<len; ++l) if(tem[(i+l)%len].hash != tem[(j+l)%len].hash) break;
        if(l>len) break;
        if(tem[(i+l)%len].hash > tem[(j+l)%len].hash) i = i+l+1;
        else j = j+l+1;
        if(i == j) j = i+1;
    }
    if(i<j) return i;
    else return j;
}

pa f[N], g[N];
int sze[N];
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[n]; 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

我们直接找哈希最大值作为起点也行(可能出题人卡了最小值23333)

# 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[N], 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], 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[N]; int rn = 0; 

namespace FACINV {
    int fac[N], inv[N], Inv[N];
    
    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[N];
    int c[N], cn = 0, deg[N]; 
    bool vis[N]; 
    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[N], g[N];
int sze[N];
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[n]; 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

 

转载于:https://www.cnblogs.com/galaxies/p/20170621_c.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值