@description@

给定一个每个点出度都为 1 的有向连通图以及 m 种颜色。求本质不同的染色方案数。

原题传送门。

@solution@

其实就是基环树。

考虑一棵有根树的本质不同染色方案:先递归处理出子树答案,通过树哈希把它长得一样的子树放在一起。

对于长得相同的子树,假如有 x 个,这子树的染色方案为 f[x],则它们的贡献为 C(f[x] + x - 1, x)。一个不难理解的组合问题。

注意那个组合数直接暴力算就好了,因为 ∑x = n。

再考虑环,可以使用 burnside 引理解决,是个经典的模型。

不过注意环旋转过后对应位置的树哈希值必须相等,否则这个置换是不合法的,不能算入最后的置换群大小中。

为了保证这一点,可以先求出所有可行循环节大小。转成 border 用 kmp 求即可。

总时间复杂度 O(nlogn)。

@accepted code@

#include <map>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std; typedef unsigned long long ull; const int MAXN = 100000;
const int MOD = int(1E9) + 7;
const int HASHMOD = 19260817; int pow_mod(int b, int p) {
int ret = 1;
for(int i=p;i;i>>=1,b=1LL*b*b%MOD)
if( i & 1 ) ret = 1LL*ret*b%MOD;
return ret;
} struct dhash{
ull h1; int h2; dhash() : h1(0), h2(0) {}
dhash(ull _h1, int _h2) : h1(_h1), h2(_h2) {}
friend dhash operator + (const dhash &a, const dhash &b) {
return dhash(a.h1 + b.h1, (a.h2 + b.h2 >= HASHMOD ? a.h2 + b.h2 - HASHMOD : a.h2 + b.h2));
}
friend dhash operator - (const dhash &a, const dhash &b) {
return dhash(a.h1 - b.h1, (a.h2 - b.h2 < 0 ? a.h2 - b.h2 + HASHMOD : a.h2 - b.h2));
}
friend dhash operator * (const dhash &a, const dhash &b) {
return dhash(a.h1 * b.h1, 1LL * a.h2 * b.h2 % HASHMOD);
}
friend bool operator < (const dhash &a, const dhash &b) {
return (a.h1 == b.h1 ? a.h2 < b.h2 : a.h1 < b.h1);
}
friend bool operator == (const dhash &a, const dhash &b) {
return ((!(a < b)) && (!(b < a)));
}
friend bool operator != (const dhash &a, const dhash &b) {
return a < b || b < a;
}
}; int f[MAXN + 5], n, m;
bool tag[MAXN + 5], vis[MAXN + 5];
int get(int x) {
vis[x] = true;
if( vis[f[x]] ) {
tag[x] = true;
return f[x];
}
int ret = get(f[x]);
if( ret != -1 )
tag[x] = true;
return (ret == x ? -1 : ret);
} dhash h[MAXN + 5], sed[MAXN + 5], pw[2*MAXN + 5];
int dp[MAXN + 5], iv[MAXN + 5], siz[MAXN + 5];
vector<int>v[MAXN + 5];
int comb(int n, int m) {
n = ((n + m) % MOD + MOD - 1) % MOD;
int ret = 1;
for(int i=1;i<=m;i++)
ret = 1LL*ret*(n-i+1)%MOD*iv[i]%MOD;
return ret;
}
pair<dhash, int>tmp[MAXN + 5];
void dfs(int x) {
siz[x] = 1, h[x] = dhash(1, 1);
for(int i=0;i<(int)v[x].size();i++) {
int to = v[x][i];
if( tag[to] ) continue;
dfs(to); siz[x] += siz[to];
h[x] = h[x] + sed[siz[to]] * h[to];
}
int tot = 0;
for(int i=0;i<(int)v[x].size();i++) {
int to = v[x][i];
if( tag[to] ) continue;
tmp[++tot] = make_pair(h[to], dp[to]);
}
sort(tmp + 1, tmp + tot + 1), dp[x] = m;
for(int i=1;i<=tot;i++) {
int j = i;
while( j < tot && tmp[i].first == tmp[j + 1].first )
j++;
dp[x] = 1LL*dp[x]*comb(tmp[i].second, j - i + 1)%MOD;
i = j;
}
}
int b[MAXN + 5], c[MAXN + 5], cnt;
int con[MAXN + 5], tot;
int gcd(int x, int y) {
return (y == 0 ? x : gcd(y, x % y));
}
int fail[MAXN + 5], prod[MAXN + 5];
int solve1() {
prod[0] = c[0];
for(int i=1;i<cnt;i++)
prod[i] = 1LL*prod[i - 1]*c[i]%MOD;
fail[0] = -1, fail[1] = 0;
for(int i=2;i<=cnt;i++) {
int j = fail[i - 1];
while( j != -1 && b[j] != b[i-1] )
j = fail[j];
fail[i] = j + 1;
}
for(int i=1;i<=cnt;i++) con[i] = 0;
for(int p=cnt;fail[p]!=-1;p=fail[p])
con[cnt - fail[p]] = prod[cnt - fail[p] - 1];
/*
for(int i=1;i<=cnt;i++) {
if( cnt % i == 0 ) {
bool flag = true;
for(int j=i;j<cnt;j++)
if( b[j-i] != b[j] ) {
flag = false;
break;
}
if( flag ) {
con[i] = 1;
for(int j=0;j<i;j++)
con[i] = 1LL*con[i]*c[j]%MOD;
}
else con[i] = 0;
}
}
*/
int ans = 0; tot = 0;
for(int i=0;i<cnt;i++) {
int x = gcd(i, cnt);
if( con[x] ) ans = (ans + con[x]) % MOD, tot++;
}
return ans;
}
int solve() {
int ret = solve1();
return 1LL*ret*pow_mod(tot, MOD-2)%MOD;
}
int a[MAXN + 5], dcnt; dhash d[MAXN + 5];
int read() {
int x = 0, ch = getchar();
while( ch > '9' || ch < '0' ) ch = getchar();
while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar();
return x;
}
void work() {
n = read(), m = read();
for(int i=1;i<=n;i++) v[i].clear(), tag[i] = vis[i] = false;
for(int i=1;i<=n;i++) v[f[i] = read()].push_back(i);
get(1);
for(int i=1;i<=n;i++)
if( tag[i] ) dfs(i);
cnt = 0;
for(int i=1;i<=n;i++)
if( tag[i] ) {
int p = i;
do {
a[cnt++] = p;
p = f[p];
}while( p != i );
break;
}
dcnt = 0;
for(int i=0;i<cnt;i++) d[++dcnt] = h[a[i]];
sort(d + 1, d + dcnt + 1), dcnt = unique(d + 1, d + dcnt + 1) - d - 1;
for(int i=0;i<cnt;i++) b[i] = lower_bound(d + 1, d + dcnt + 1, h[a[i]]) - d, c[i] = dp[a[i]];
printf("%d\n", solve());
}
ull get_rand() {
ull p = rand() << 16 | rand();
ull q = rand() << 16 | rand();
return p << 32 | q;
}
void init() {
for(int i=0;i<=MAXN;i++) {
ull p = get_rand();
sed[i] = dhash(p, int(p % HASHMOD));
}
iv[1] = 1;
for(int i=2;i<=MAXN;i++)
iv[i] = MOD - 1LL*(MOD/i)*iv[MOD%i]%MOD;
pw[0] = dhash(1, 1), pw[1] = dhash(1313131, 1313131);
for(int i=2;i<=2*MAXN;i++)
pw[i] = pw[i-1] * pw[1];
}
int main() {
srand(20041112), init();
int T; scanf("%d", &T);
while( T-- ) work();
}

@details@

貌似有点小卡常,不知道是不是我常数太大。

@hdu - 5822@ color的更多相关文章

  1. hdu 1556:Color the ball(第二类树状数组 —— 区间更新,点求和)

    Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  2. hdu 1556:Color the ball(线段树,区间更新,经典题)

    Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  3. HDU.1556 Color the ball (线段树 区间更新 单点查询)

    HDU.1556 Color the ball (线段树 区间更新 单点查询) 题意分析 注意一下pushdown 和 pushup 模板类的题还真不能自己套啊,手写一遍才行 代码总览 #includ ...

  4. hdu 1199 Color the Ball

    http://acm.hdu.edu.cn/showproblem.php?pid=1199 Color the Ball Time Limit: 2000/1000 MS (Java/Others) ...

  5. HDU 1556 Color the ball (数状数组)

    Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  6. HDU 1556.Color the ball-差分数组-备忘

    备忘. 差分数组: 区间更新查询有很多方法,线段树.树状数组等都可以.如果为离线查询,就可以考虑使用差分数组. 假设对于区间[l,r]的每个数都加1,我们用一个数组a来记录,a[l]+=1;a[r+1 ...

  7. 线段树(求单结点) hdu 1556 Color the ball

    Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  8. hdu 1556 Color the ball(区间更新,单点求值)

    Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  9. HDU 1556 Color the ball(线段树区间更新)

    Color the ball 我真的该认真的复习一下以前没懂的知识了,今天看了一下线段树,以前只会用模板,现在看懂了之后,发现还有这么多巧妙的地方,好厉害啊 所以就应该尽量搞懂 弄明白每个知识点 [题 ...

随机推荐

  1. 日志文件的配置----【logback-spring.xml】

    一.引入相关包 <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-c ...

  2. Mac node.js express-generator脚手架安装

    前言 由于本人在学习NodeJs的express框架时,在Mac电脑上安装express遇到了一个深痛的坑点,特写此文来记录.该坑点的解决方案我在国内的度娘没有找到,问别人也没有方案,最后通过goog ...

  3. OpenResty高性能web平台

    openresty高性能web平台安装使用 简介:OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库.第三方模块以及大多数的依赖项.用 ...

  4. C语言关于数据类型转换

    自动类型转换 自动类型转换就是编译器默默地.隐式地.偷偷地进行的数据类型转换,这种转换不需要程序员干预,会自动发生. 1) 将一种类型的数据赋值给另外一种类型的变量时就会发生自动类型转换,例如: ; ...

  5. python 比较常见的工具方法

    下面是一些工作过程中比较常见的工具方法,但不代表最终答案.希望能对你有所帮助,如果您有更好更多的方法工具,欢迎推荐! 1. 按行读取带json字符串的文件 # -*- coding:utf-8 -*- ...

  6. Java使用Netty实现简单的RPC

    造一个轮子,实现RPC调用 在写了一个Netty实现通信的简单例子后,萌发了自己实现RPC调用的想法,于是就开始进行了Netty-Rpc的工作,实现了一个简单的RPC调用工程. 如果也有兴趣动手造轮子 ...

  7. matlab自学笔记

    1.字符串格式化,用sprintf如a=sprintf('%.2f_除以%d等于%.3f',1.5,2,0.75)%则a=1.50除以2等于0.750 2.for循环只能针对整数,不能遍历字符串或其他 ...

  8. ElementUi 全选功能实现

    每次使用官方的示例都会被坑一下(可能是我的理解不够透彻吧,不记录一下保不准下次依旧会忘), 故此今天做个Demo 记录一下.本次仍然使用官网给出的示例,只做理解性的说明. 1.此处先给出官方示例代码: ...

  9. 【Tomcat】JSP使用Session、Cookie实现购物车

    购物界面shop.jsp 初始页面 添加商品后,在session中设置属性,重定向回到shop.jsp,然后根据session的内容显示结果 Cookie设置setMaxAge可以延长session的 ...

  10. Java并发编程 (五) 线程安全性

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.安全发布对象-发布与逸出 1.发布与逸出定义 发布对象 : 使一个对象能够被当前范围之外的代码所使用 ...