【NOI2013模拟】坑带的树

  • 题意:

    • 求\(n\)个点,\(m\)条边的同构仙人球个数.
    • \(n\le 1000\)
  • 这是一道怎么看怎么不可做的题。

  • 这种题,肯定是圆方树啦~

  • 好,那么首先转为广义圆方树.

  • 圆方树上有两种点(废话),那么对于一个方点,它实际上代表的是一个点双,所以我们需要判断一个方点的子树是否中间对称,如果对称则这个子树答案乘\(2\).

  • 显然.

  • 然后判断一个圆点与几个方点相连时,注意到方点之间是可以互相交换顺序的,于是我们看看有多少个子树相同,乘个阶乘.

  • 最后就是求同构仙人球的个数了.

  • 这个比较麻烦,但也不难。。

  • 实质上我们可以求一个\(Hash\)值,每次从一个点的父亲节点对应的那条边开始找,找到最后一条边,然后再从第一条边开始找,找到父亲那条边.

  • 这样,对于单独一个环,在环内的两个点,父亲的那条边始终是相同的,所以我们就正反扫一遍找到的边,然后取个Hash值的\(min\)

  • 实现比较困难,但需要奋斗!

  • 这里有个小trick,那就是当我们缩点双时,一定要按照一个固定的顺序去缩,具体代码里会有说。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm> #define I register int
#define F(i, a, b) for (I i = a; i <= b; i ++)
#define G(i, a, b) for (I i = a; i >= b; i --)
#define mem(a, b) memset(a, b, sizeof a)
#define mec(a, b) memcpy(a, b, sizeof a)
#define mn(a, b) ((a) = (a) < (b) ? (a) : (b))
#define mx(a, b) ((a) = (a) > (b) ? (a) : (b))
#define Get getchar() const int N = 2010, M = 30 * N, Z[5] = {1532531, 12136371, 1232535, 1846315, 1463322}, Mo[5] = {16232312, 12322323, 12552344, 74556263, 13223623}, mo = 1000000003LL; using namespace std; int n, m, x, y, answer, bz, New, cnt, top, rt, len, dfn[M], low[M], d[M], size[N], Ans[N][5], HASH[N][5], hash[N][5], ans[M], H[M], jc[M], fath[M], vis[M];
int tov[M], nex[M], las[M], tot;
int To[M], Nx[M], Ls[M], TOT;
struct node { int v , num; } D[M]; void R(I &x) {
char c = Get; x = 0; I t = 1;
for (; !isdigit(c); c = Get) t = (c == '-' ? -1 : t);
for (; isdigit(c); x = (x << 3) + (x << 1) + c - '0', c = Get); x *= t;
} bool cmp(node x, node y) { return x.v < y.v; } void ins(I x, I y) { tov[ ++ tot ] = y, nex[ tot ] = las[x], las[x] = tot; } void link(I x, I y) {
To[++ TOT] = y, Nx[TOT] = Ls[x], Ls[x] = TOT;
To[++ TOT] = x, Nx[TOT] = Ls[y], Ls[y] = TOT;
}
void Tarjan(I k, I edge) {
dfn[k] = low[k] = ++ cnt, d[++ top] = k;
for (I x = las[k]; x; x = nex[x])
if (!dfn[tov[x]]) {
Tarjan(tov[x], x), mn(low[k], low[tov[x]]);
if (low[tov[x]] >= dfn[k]) {
link(k, ++ New), link(d[top --], New);
while (d[top + 1] ^ tov[x]) link(d[top --], New);
//我最后两句话想说的就是这个地方,千万不能打成这样:
//link(d[top --], ++ New), link(k, New);
//while (d[top + 1] ^ tov[x]) link(d[top --], New);
//为什么不必多说,意会意会
}
} else if (x ^ (edge ^ 1)) mn(low[k], dfn[tov[x]]);
} void GetHash(I k, I fa) {
size[k] ++;
for (I x = Ls[k] ; x ; x = Nx[x])
if (To[x] ^ fa) GetHash(To[x], k), size[k] += size[To[x]];
for (len = 0, x = Ls[k]; x ; x = Nx[x])
if (To[x] ^ fa) D[++ len] = {size[To[x]], To[x]};
sort(D + 1, D + len + 1, cmp);
F(i, 0, 4) {
HASH[k][i] = 1;
F(j, 1, len) HASH[k][i] = (1LL * HASH[k][i] * D[j].v + 1LL * HASH[D[j].num][i] * Z[i] + 1LL * D[j].v * Z[i]) % Mo[i];
}
} bool Good(I p, I x, I q, I y) {
F(i, 0, 4) if (HASH[x][i] ^ HASH[y][i]) return 1;
return 0;
} void GetAns(I k, I fa) {
ans[k] = 1;
for (I x = Ls[k] ; x ; x = Nx[x])
if (To[x] ^ fa) GetAns(To[x], k), ans[k] = (1LL * ans[k] * ans[To[x]]) % mo;
for (bz = 1, len = 0, x = Ls[k]; x; x = Nx[x])
if (To[x] ^ fa) H[++ len] = To[x];
if (k > n) {
F(i, 1, len / 2)
if (Good(rt, H[i], rt, H[len - i + 1])) { bz = false; break; }
if (bz && (len / 2)) ans[k] = (ans[k] * 2LL) % mo;
}
else
{
F(i, 1, len) D[i] = {HASH[H[i]][0], H[i]};
sort(D + 1, D + len + 1, cmp), cnt = 1;
F(i, 2, len)
if (!Good(rt, D[i].num, rt, D[i - 1].num)) cnt ++; else ans[k] = (1LL * ans[k] * jc[cnt]) % mo, cnt = 1;
ans[k] = (1LL * ans[k] * jc[cnt]) % mo;
}
} void GotHash(I k, I fa) { I y = 0;
for (I x = Ls[k]; x ; x = Nx[x])
if (To[x] ^ fa) GotHash(To[x], k); else y = x; //像我刚刚说的一样,从父亲那条边开始找起来,huhuhu
for (len = 0, x = Nx[y]; x ; x = Nx[x])
D[++ len] = { hash[To[x]][0], To[x] };
for (x = Ls[k] ; x ^ y; x = Nx[x])
if (To[x] ^ fa) D[++ len] = { hash[To[x]][0], To[x] }; if (k <= n) { //圆点和方点分开讨论哟
sort(D + 1, D + len + 1, cmp);
F(i, 0, 4) {
hash[k][i] = 1;
F(j, 1, len) hash[k][i] = (1LL * hash[k][i] * D[j].v + 1LL * hash[D[j].num][i] * Z[i] + 1LL * D[j].v * Z[i]) % Mo[i];
}
}
else
F(i, 0, 4) {
x = y = 1;
F(j, 1, len) x = (1LL * x * D[j].v + 1LL * hash[D[j].num][i] * Z[i] + 1LL * D[j].v * Z[i]) % Mo[i];
G(j, len, 1) y = (1LL * y * D[j].v + 1LL * hash[D[j].num][i] * Z[i] + 1LL * D[j].v * Z[i]) % Mo[i];
hash[k][i] = min(x, y);
}
} int main() {
R(n), R(m), tot = 1, New = n;
F(i, 1, m) R(x), R(y), ins(x, y), ins(y, x); Tarjan(1, 0), GetHash(1, 0); jc[0] = 1; F(i, 1, n) jc[i] = (1LL * jc[i - 1] * i) % mo; rt = 1, GetAns(rt, 0), answer = ans[rt], cnt = 1; //一下就是求同构仙人球的个数了
GotHash(rt, 0), mec(Ans, hash);
F(i, 1, n) {
if (i == rt) continue; GotHash(i, 0), bz = 0;
F(j, 0, 4)
if (hash[i][j] ^ Ans[rt][j]) { bz = 1; break; }
if (!bz) cnt ++;
} printf("%d\n", (1LL * answer * cnt) % mo);
}

【NOI2013模拟】坑带的树(仙人球的同构+圆方树乱搞+计数+HASH)的更多相关文章

  1. CF487E Tourists + 圆方树学习笔记(圆方树+树剖+线段树+multiset)

    QWQ果然我已经什么都学不会的人了. 这个题目要求的是图上所有路径的点权和!QWQ(我只会树上啊!) 这个如果是好啊 这时候就需要 圆方树! 首先在介绍圆方树之前,我们先来一点简单的前置知识 首先,我 ...

  2. [JZOJ 5909] [NOIP2018模拟10.16] 跑商(paoshang) 解题报告 (圆方树)

    题目链接: https://jzoj.net/senior/#contest/show/2529/2 题目: 题目背景:尊者神高达很穷,所以他需要跑商来赚钱题目描述:基三的地图可以看做 n 个城市,m ...

  3. 【学习笔记】圆方树(CF487E Tourists)

    终于学了圆方树啦~\(≧▽≦)/~ 感谢y_immortal学长的博客和帮助 把他的博客挂在这里~ 点我传送到巨佬的博客QwQ! 首先我们来介绍一下圆方树能干什么呢qwq 1.将图上问题简化到树上问题 ...

  4. bzoj3331 压力(圆方树)

    题目链接 圆方树 圆方树就是对于联通无向图中的每一个点双新建一个方点,与点双中的每个点连一条边,然后将原来的边删去.将原来的点看作圆点,新建的点看作方点.所以叫做圆方树. 性质 1.圆方树肯定是棵树( ...

  5. BZOJ5329:[SDOI2018]战略游戏(圆方树,虚树)

    Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着 ...

  6. 【APIO 2018】铁人两项(圆方树)

    题目链接 题意大概是,求有多少三元组$(s,c,f)(s \neq c, c \neq f, s \neq f)$,满足从$s$到$f$有一条简单路径经过$c$. 得到结论: 点双中任意互不相同的三个 ...

  7. 洛谷P4630 [APIO2018] Duathlon 铁人两项 【圆方树】

    题目链接 洛谷P4630 题解 看了一下部分分,觉得树的部分很可做,就相当于求一个点对路径长之和的东西,考虑一下能不能转化到一般图来? 一般图要转为树,就使用圆方树呗 思考一下发现,两点之间经过的点双 ...

  8. 圆方树&广义圆方树[学习笔记]

    仙人掌 圆方树是用来解决仙人掌图的问题的,那什么是仙人掌图呢? 如图,不存在边同时属于多个环的无向连通图是一棵仙人掌 圆方树 定义 原先的仙人掌图,通过一些奇妙的方法,可以转化为一棵由圆点,方点和树边 ...

  9. 洛谷4630APIO2018铁人两项(圆方树+dp)

    QWQ神仙题啊(据说是今年第一次出现圆方树的地方) 首先根据题目,我们就是求对于每一个路径\((s,t)\)他的贡献就是两个点之间的点数,但是图上问题我并没有办法很好的解决... 这时候考虑圆方树,我 ...

随机推荐

  1. Django数据库操作中You are trying to add a non-nullable field 'name' to contact without a default错误处理

    name = models.CharField(max_length=50) 执行:python manage.py makemirations出现以下错误: You are trying to ad ...

  2. 创建简单的表单Demo

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.mybatis.spring.mapper.MapperScannerConfigurer#0'

    七月 05, 2018 10:26:54 上午 org.apache.tomcat.util.digester.SetPropertiesRule begin警告: [SetPropertiesRul ...

  4. Lodop输出页面input文本框的最新值

    默认使用Lodop打印页面上的文本框等,会发现虽然页面上文本框输入了值,打印预览却是空的,这是由于没有把最新的值传入Lodop. 如图,演示的是Lodop如何输出文本框内的新值,这里整个页面只有inp ...

  5. Draw your Next App Idea with Ink to Code

    Imagine that you’ve just been struck by inspiration for your next great app. You might start by jott ...

  6. Tcp协议的keepalive功能

    L:128

  7. windows新增/修改/删除系统环境变量bat示例,一键配置JAVA_HOME

    setx JAVA_HOME "C:\Program Files\java\jdk1.6.0_27" /m setx classpath = ".;%JAVA_HOME% ...

  8. 性能测试工具 Locust

    https://docs.locust.io/en/latest/quickstart.html

  9. 【XSY2524】唯一神 状压DP 矩阵快速幂 FFT

    题目大意 给你一个网格,每个格子有概率是\(1\)或是\(0\).告诉你每个点是\(0\)的概率,求\(1\)的连通块个数\(\bmod d=0\)的概率. 最开始所有格子的概率相等.有\(q\)次修 ...

  10. 【XSY1594】棋盘控制 概率DP

    题目描述 给你一个\(n\times m\)的棋盘,每次随机在棋盘上放一个国际象棋中的车,不能和以前放的重叠.每个车可以控制当前行和当前列.当所有行和所有列都被控制时结束游戏.问你结束时期望放了多少个 ...