zoukankan      html  css  js  c++  java
  • 【NOI2013模拟】坑带的树(仙人球的同构+圆方树乱搞+计数+HASH)

    【NOI2013模拟】坑带的树

    • 题意:

      • (n)个点,(m)条边的同构仙人球个数.
      • (nle 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
    ", (1LL * answer * cnt) % mo);
    }
    
  • 相关阅读:
    028 Android 旋转动画+病毒查杀效果+自定义样式的ProgressBar
    027 Android 可扩展的listview:ExpandableListView的使用案例
    026 Android 带不同类型条目的listview(纯文本类型的条目,图片+文字类型的条目)+读取内存空间、手机进程信息+常驻悬浮框
    025 Android 带进度条的对话框(ProgressDialog)
    024 Android 自定义样式对话框(AlertDialog)
    023 Android 自定义Toast控件
    Cordova-安装Cordova过程详细解
    Symfony3 查询搜索功能DQL语句like查询
    迭代式开发使用方法总结
    require.js vs browserify
  • 原文地址:https://www.cnblogs.com/Pro-king/p/9383451.html
Copyright © 2011-2022 走看看