【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. Front-end Job Interview Questions

    Front-end Job Interview Questions 前端面试 https://github.com/h5bp/Front-end-Developer-Interview-Questio ...

  2. 老男孩python学习自修【第三天】列表用法

    列表的使用: list.append(value) 向列表增加元素 list.insert(index, value) 向列表指定元素插入元素 list.extend(newlist) 用新的列表扩展 ...

  3. thinkphp视图中插入php代码

    性别: <?php if($item['sex'] == 1):?> 男 <?php else:?> 女 <?php endif;?> 错误:<?php ec ...

  4. Nginx log_format

    L11 nginx 官网的日志格式如下 log_format compression(自定义名称) '$remote_addr - $remote_user [$time_local] ' '&quo ...

  5. LIRE图片识别搜索demo--Ubuntu开发

    Ubuntu安装shadowsocks客户端/服务端教程 1.安装shadowsocks sudo apt-get update sudo apt-get install python-pip sud ...

  6. Spring MVC启动过程(1):ContextLoaderListener初始化

    此文来自https://my.oschina.net/pkpk1234/blog/61971 (写的特别好)故引来借鉴 Spring MVC启动过程 以Tomcat为例,想在Web容器中使用Spirn ...

  7. 51Nod1778 小Q的集合 【组合数】【Lucas定理】

    题目分析: 题解好高深...... 我给一个辣鸡做法算了,题解真的看不懂. 注意到方差恒为$0$,那么其实就是要我们求$\sum_{i=0}^{n}\binom{n}{i}(i^k-(n-i)^k)^ ...

  8. Codeforces Round #505 Div. 1 + Div. 2

    传送门:>Here< 从来没打过\(CF\)(由于太晚了)-- 不知道开学了以后有没有机会能够熬夜打几场,毕竟到现在为止都是\(unrated\)好尴尬啊~ 今天早上打了几题前几天的比赛题 ...

  9. thymeleaf中的判断总结

    判断String字符串,添加引号 th:class="${flag=='forum.html'}?'active'" 判断boolean类型,注意不能当成字符串处理,不能添加引号 ...

  10. 自学华为IoT物联网_11 物联网操作系统介绍

    点击返回自学华为IoT物流网 自学华为IoT物联网_11 物联网操作系统介绍 1.1  物联网面临的困难 物联网终端发展面临的困难:开发者需要懂硬件和芯片的差异,自行适配硬件接口 物联网开发面临的困难 ...