【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. JQ获取URL中是否含有某个字符的话,对页面进行某种操作

    一.//JQ获取URL中是否含有某个字符的话,对页面进行某种操作 例:如果URL中含有xia的字符,就在页面引入一个cssvar str=window.location.href; //获取地址栏UR ...

  2. Google浏览器解决编码乱码问题

    新版google浏览器编码乱码没有设置的入口,怎么办呢?. 步骤一: 可以下载goole的插件,名为charset,下载后的文件名为Charset_v0.4.1 步骤二: google右上角-> ...

  3. Python2基础

    1.python 3.python函数 python的函数定义: 以def关键字定义一个函数: 参数放在小括号里面: 必须有return语句: 关键字参数: 即调用函数时传参顺序可以人为指定 默认参数 ...

  4. rabbitmq 配置

    1, 安装 apt-get install rabbitmq-server -y 2, 打开管理页面 sudo rabbitmq-plugins enable rabbitmq_management ...

  5. How to install Lion on PC

    open 'InstallESD.dmg' open '/Volumes/Mac OS X Install ESD/BaseSystem.dmg' rm '/Volumes/Mac OS X Base ...

  6. solr部署tomcat 访问HTTP Status 403 – Access to the requested resource has been denied

    -----------解决403错误看这里!!-----------打开****\Tomcat 8.5\webapps\solr\WEB-INF里面的web.xml,把下面这段配置注释掉!!!如下所示 ...

  7. vue的 v-for 循环中图片加载路径问题

    先看一下产品需求,如下图所示, 产品要求图片和它的名称一一对应,本来是非常简单的需求,后台直接返回图片路径和名称,前台直接读取就可以了,但是我们没有存储图片的服务器,再加上是一个实验性的需求,图片需要 ...

  8. Nginx memcached应用层反向代理

    L:105 Module ngx_http_memcached_module 模块默认编译进Nginx

  9. 使用binlog,实现MySQL数据恢复

    mysql的binlog日志,用于记录数据库的增.删.改等修改操作,默认处于关闭状态.使用binlog实现数据恢复的条件为 1.binlog日志功能已开启 2.若binlog在数据库创建一段时候后开启 ...

  10. Nginx+Tomcat 负载均衡集群

    案例分析 通常情况下,一台Tomcat站点由于可能出现单点故障及无法应对多客户复杂多样性的请求等问题,不能单独应用于生产环境下,所以我们需要一套更可靠的解决方案来完善Web站点架构. Nginx是一款 ...