【知识小结】圆方树 && 广义圆方树

关于仙人掌的总结

immortalCO的博客

yyb的博客

模板

namespace T{
	vector <int> e[maxn * 2];
	int tag[maxn * 2];
	void adde(int x,int y){  //cout<
		e[x].PB(y) , e[y].PB(x); }
}
namespace G{
	node e[maxn * 2];
	int head[maxn],cnt,dfn[maxn],fa[maxn],dfstime,used[maxn];

	void adde(int x,int y){
	//	if ( x < y ) cout<
		e[++cnt].to = y;
		e[cnt].next = head[x];
		head[x] = cnt;
	}
	void dfs(int x){
		dfn[x] = ++dfstime;
		for(int i = head[x] ; i ; i = e[i].next){
			if ( e[i].to == fa[x] ) continue;
			if ( dfn[e[i].to] ){
				if ( dfn[e[i].to] > dfn[x] ) continue;
				//find_circle
				++tot , T::tag[tot] = 1;
				int z = x;
				while ( z != e[i].to ){
					T::adde(tot,z);
					used[z] = 1;
					z = fa[z];
				}
				T::adde(e[i].to,tot);
			}
			else{
			 	fa[e[i].to] = x, dfs(e[i].to);
			 	if ( !used[e[i].to] ) used[e[i].to] = 1 , T::adde(x,e[i].to);
			}
		}
	}
}

例题

仙人掌同构

po姐题解
注意环上点是有序的,而树上子树无序。
环有翻转同构,环为根的话还有旋转同构

代码是NEERC 13 C
数据范围大一点

//仙人掌同构数
#include
using namespace std;
#define PB push_back
#define lowbit(x) (x&(-x))
#define MP make_pair
#define fi first
#define se second
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define rep(i,l,r) for (register int i = l ; i <= r ; i++)
#define down(i,r,l) for (register int i = r ; i >= l ; i--)
#define fore(i,x) for (int i = head[x] ; i ; i = e[i].next)
#define SZ(v) (int)v.size()

typedef long long ll;
typedef pair <int,int> pr;
const int maxn = 1e6 + 10;
const int inf = 1e8;

const ll MOD = 180143985094819841ll;
//const ll MOD = 1e9 + 7;
const ll C1 = 35224111;
const ll C2 = 33352437;
const ll C3 = 70525121; 

ll mul_C2[maxn],inv_C2[maxn];
int prime[maxn],cnt,tag[maxn],mn_p[maxn];

ll mul(ll a,ll b){
//	return a * b % MOD;
    return (a * b - (ll)(a / (long double)MOD * b + 1e-3)* MOD + MOD) % MOD;
//	return (__int128)a * b % MOD;
}
ll power(ll x,ll y){
    ll res = 1;
    while ( y ) {
        if ( y & 1 ) res = mul(res,x);
        x = mul(x,x);
        y >>= 1;
    }
    return res;
}
void init(){
	ll inv = power(C2,MOD - 2);
	mul_C2[0] = inv_C2[0] = 1;
	for (int i = 1 ; i < maxn ; i++){
		mul_C2[i] = mul(mul_C2[i - 1],C2);
		inv_C2[i] = mul(inv_C2[i - 1],inv);
	}
	for (int i = 2 ; i < maxn ; i++){
		if ( !tag[i] ) prime[++cnt] = i , mn_p[i] = i;
		for (int j = 1 ; j <= cnt && prime[j] * i < maxn ; j++){
			tag[i * prime[j]] = 1;
			mn_p[i * prime[j]] = prime[j];
			if ( i % prime[j] == 0 ) break;
		}
	}
}

map <int,int> ans; 
int tot,n,m;

struct node{
	int next,to,w;
};
void add_to_ans(int num){
	
	while ( num > 1 ){
		int c = 0 , cp = mn_p[num];
		while ( num % cp == 0 ) num /= cp , c++;
		ans[cp] += c;
	}
}
namespace T{
	vector <int> e[maxn * 2];
	int mx[maxn * 2],sz[maxn * 2],tag[maxn * 2];
	pr id[2] = {{inf,0},{inf,0}};

	void adde(int x,int y){  //cout<
		e[x].PB(y) , e[y].PB(x); }
	void find_g(int x,int fa){
		sz[x] = 1;
		for (auto y : e[x]){
			if ( y == fa ) continue;
			find_g(y,x);
			sz[x] += sz[y];
			mx[x] = max(mx[x],sz[y]);
		}
	}
	ll get_hash_tree(vector <ll> &vec){
		ll res = 1240198;
		for (auto x : vec){
			res = (res + power(C1,x)) % MOD;
		}
		return res;
	}
	ll get_hash_cyc(vector <ll> &vec){
		ll res = 3982003; ll q = 1;
		for (auto x : vec){
			q = mul(q,C2);
			res = (res + mul(q,power(C3,x))) % MOD;
		}
		return res;
	}
	vector <ll> all_cycles(vector <ll> &vec){
		vector <ll> item(SZ(vec) * 2);
		for (int i = 0 ; i < SZ(vec) ; i++){
			item[i] = mul(mul_C2[i + 1],power(C3,vec[i]));
		}
		for (int i = 0 ; i < SZ(vec) ; i++){
			item[i + SZ(vec)] = mul(mul_C2[i + 1 + SZ(vec)],power(C3,vec[i]));
		}
		for (int i = 1 ; i < SZ(item) ; i++) item[i] = (item[i] + item[i - 1]) % MOD;
		vector <ll> res(SZ(vec));
		for (int i = 0 ; i < SZ(vec) ; i++){
			ll d = (item[i + SZ(vec) - 1] - (i ? item[i - 1] : 0) + MOD) % MOD;
			d = mul(inv_C2[i],d);
			res[i] = d;
		}
		return res;
	}
	ll hash_cactus(int x,int fa){
		vector <ll> vec;
		if ( tag[x] ){
			if ( fa == 0 ){
				for (auto y : e[x])
					vec.PB(hash_cactus(y,x));
				vector <ll> h = all_cycles(vec);
				int num = 0;
				for (auto y : h) if ( y == h[0] ) num++;
				reverse(vec.begin(),vec.end());
				vector <ll> h2 = all_cycles(vec);
				for (auto y : h2) if ( y == h[0] ) num++;
				if ( num > 1 ) add_to_ans(num);
				return 0;
			}
			else{
				int up = -1;
				for (int i = 0 ; i < SZ(e[x]) ; i++){
					if ( e[x][i] == fa ){ up = i; break; }
				}
				assert(up != -1);
				for (int i = up + 1 ; i < SZ(e[x]) ; i++){
					vec.PB(hash_cactus(e[x][i],x));
				}
				for (int i = 0 ; i < up ; i++){
					vec.PB(hash_cactus(e[x][i],x));
				}
				ll h1 = get_hash_cyc(vec);
				reverse(vec.begin(),vec.end());
				ll h2 = get_hash_cyc(vec);
				if ( h1 == h2 ) ans[2]++;
				return min(h1,h2);
			}
		}
		else{
			for (auto y : e[x]){
				if ( y == fa ) continue;
				vec.PB(hash_cactus(y,x));
			}
			sort(vec.begin(),vec.end());
			int cur = 1;
			for (int i = 1 ; i < SZ(vec) ; i++){
				if ( vec[i] != vec[i - 1] ) cur = 0;
				++cur;
				if ( cur > 1 ) add_to_ans(cur);
			}
			return get_hash_tree(vec);
		}
	}
	void solve(){
		find_g(1,0);
		for (int i = 1 ; i <= tot ; i++){
			int cur = max(mx[i],tot - sz[i]);
			if ( id[0].fi > cur ) id[0] = {cur,i} , id[1] = {inf,0};
			else if ( id[1].fi > cur ) id[1] = {cur,i};
		}
	//	cout<
		if ( id[0].fi == id[1].fi ){
			ll h1 = hash_cactus(id[0].se,id[1].se);
			ll h2 = hash_cactus(id[1].se,id[0].se);
			if ( h1 == h2 ) ans[2]++;
		}
		else hash_cactus(id[0].se,0);
	}
}

namespace G{
	node e[maxn * 2];
	int head[maxn],cnt,dfn[maxn],fa[maxn],dfstime,used[maxn];

	void adde(int x,int y){
	//	if ( x < y ) cout<
		e[++cnt].to = y;
		e[cnt].next = head[x];
		head[x] = cnt;
	}
	void dfs(int x){
		dfn[x] = ++dfstime;
		for(int i = head[x] ; i ; i = e[i].next){
			if ( e[i].to == fa[x] ) continue;
			if ( dfn[e[i].to] ){
				if ( dfn[e[i].to] > dfn[x] ) continue;
				//find_circle
				++tot , T::tag[tot] = 1;
				int z = x;
				while ( z != e[i].to ){
					T::adde(tot,z);
					used[z] = 1;
					z = fa[z];
				}
				T::adde(e[i].to,tot);
			}
			else{
			 	fa[e[i].to] = x, dfs(e[i].to);
			 	if ( !used[e[i].to] ) used[e[i].to] = 1 , T::adde(x,e[i].to);
			}
		}
	}
}

int main(){
	freopen("cactus.in","r",stdin);
	freopen("cactus.out","w",stdout);
//	freopen("input.txt","r",stdin);
	scanf("%d %d",&n,&m);
	for (int i = 1 ; i <= m ; i++){
	/*	int x,y;
		scanf("%d %d",&x,&y);
		G::adde(x,y) , G::adde(y,x);*/
		int k,x,y = 0;
		scanf("%d",&k);
		for (int i = 1 ; i <= k ; i++){
			scanf("%d",&x);
			if ( y ) G::adde(x,y) , G::adde(y,x);
			y = x;
		}
	}
	init();
	tot = n;
	G::dfs(1);
	//cout<<"solve\n";
	T::solve();
	if ( ans.size() == 0 ) puts("0");
	else{
		printf("%d\n",ans.size());
		for (auto y : ans){
			printf("%d %d\n",y.fi,y.se);
		}
	/*	ll as = 1 , mod = 1e9 + 3;
		for (auto y : ans){
			for (int k = 0 ; k < y.se ; k++) as = as * y.fi % mod;
		}
		cout<
	}
}

杂事:datamaker

感觉仙人掌好难造,我是在树上随机加边

#include
using namespace std;

#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) ((x)&(-(x)))

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;

const ld inf = 2e18;
const int N = 3e6 + 10;
const int maxn = 100020;
const ll mod = 1e9 + 7;

inline ll power(ll x,ll y){
	y = ((y % (mod - 1)) + (mod - 1)) % (mod - 1);
//	if ( y < 0 ) return power(power(x,-y),mod - 2);
	ll res = 1;
	while ( y ){
		if ( y & 1 ) res = res * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return res;
}

set <pr> s;
vector <int> e[maxn];
int fa[maxn],dth[maxn],tag[maxn];

void adde(int x,int y){
	e[x].pb(y);
	e[y].pb(x);
}
void dfs(int x){
	rvc(i,e[x]){
		int to = e[x][i];
		if ( to == fa[x] ) continue;
		fa[to] = x , dth[to] = dth[x] + 1;
		dfs(to);
	}
}
bool lca(int x,int y){
	int flag = 0;
	while ( x != y ){
		if ( dth[x] < dth[y] ) swap(x,y);
		flag |= tag[x] , x = fa[x];
	}
	flag |= tag[x]; //只要求边不在多个环上,删掉这句话
	return flag ^ 1;
}
void cov(int x,int y){
	while ( x != y ){
		if ( dth[x] < dth[y] ) swap(x,y);
		tag[x] = 1 , x = fa[x];
	}
	tag[x] = 1; //只要求边不在多个环上,删掉这句话
}
int main(){
	freopen("1.cnt","r",stdin);
	int cnt;
	scanf("%d",&cnt);
	fclose(stdin);
	freopen("1.cnt","w",stdout);
	cout<<++cnt;
	fclose(stdout);
	srand(cnt + time(0));
	freopen("input.txt","w",stdout);
	int n = 20,m = n + rand() % 2;
	cout<<n<<" "<<m<<endl;
	rep(i,1,n) cout<<rand() % 100 + 1<<" ";
	cout<<endl;
	rep(i,2,n){
		int x = rand() % (i - 1) + 1;
		adde(i,x);
		cout<<i<<" "<<x<<endl;
		s.insert(mp(x,i)) , s.insert(mp(i,x));
	}
	dfs(1);
	rep(i,1,m - n + 1){
		int x = rand() % n + 1 , y = rand() % n + 1;
		while ( x == y || !lca(x,y) || s.find(mp(x,y)) != s.end() ) x = rand() % n + 1 , y = rand() % n + 1;
		cov(x,y) , s.insert(mp(x,y)) , s.insert(mp(y,x));
		cout<<x<<" "<<y<<endl;
	}

}

1. 求仙人掌最大权独立集

一道40min就应该ok的圆方树DP。
犯了两个SB错误!
1. 特判方点忘了往下继续dfs
2. 往下dfs但是环上DP用的数组标号,数组被下面的点修改
都是非常简单。但我经常犯的错误!
从训练到下来总共调了1小时20分钟,非常浪费时间!

#include
using namespace std;
#define pb push_back
#define rep(i,l,r) for (int i = l ; i <= r ; i++)

const int maxn = 3e5 + 20;
const int inf = 1e9;

vector <int> e[maxn],e2[maxn];
vector <int> cur;

int tops,n,m,dfn[maxn],low[maxn],tot,tag[maxn],val[maxn],dfstime,st[maxn];
int f[maxn][2],h[maxn][2][2],g[2][2];

void adde(int x,int y){
	e[x].pb(y);
	e[y].pb(x);
}
void adde2(int x,int y);
void tarjian(int x,int fa){
	dfn[x] = low[x] = ++dfstime;
	for (int i = 0 ; i < e[x].size() ; i++){
		int to = e[x][i];
		if ( to == fa ) continue;
		if ( !dfn[to] ){
			st[++tops] = to;
			tarjian(to,x);
			low[x] = min(low[to],low[x]);
			if ( low[to] >= dfn[x] ){
				cur.clear();
				do{
					cur.pb(st[tops]);
				}while ( st[tops--] != to );
				if ( cur.size() == 1 ){
					adde2(x,cur[0]);
				}
				else if ( cur.size() > 1 ){
					tag[++tot] = 1;
					adde2(x,tot);
					for (int i = 0 ; i < cur.size() ; i++) adde2(tot,cur[i]);
					//for (int i = 0  ; i < cur.size() ; i++) cout<
					//	cout<<"end circle\n";
				}
			}
		}
		else{
			low[x] = min(dfn[to],low[x]);
		}
	}	
}
#define e e2
void adde2(int x,int y){
//	cout<
	e[x].pb(y);
	e[y].pb(x);
}
void build(){
	tot = n;
	for (int i = 1 ; i <= n ; i++) if ( !dfn[i] ) tarjian(i,0);
	//for (int i = 1 ; i <= n ; i++) cout<
}
void dfs(int x,int fa){
	if ( !tag[x] ){
		f[x][0] = 0 , f[x][1] = val[x];
		for (int i = 0 ; i < e[x].size() ; i++){
			int to = e[x][i];
			if ( to == fa ) continue;
			dfs(to,x);
			if ( !tag[to] ){
				int w = max(f[to][1],f[to][0]);
				//g[x][0] = f[x][0] , g[x][1] = f[x][1] , f[x][0] = f[x][1] = 0;
				f[x][0] += w;
				f[x][1] += f[to][0];
			}
			else{
				f[x][0] += f[to][0];
				f[x][1] += f[to][1] - val[x];
			}
		}	
		return;
	}
	h[x][0][1] = h[x][1][0] = -inf , h[x][0][0] = 0 , h[x][1][1] = val[e[x][0]];
	for (int i = 1 ; i < e[x].size() ; i++){
		int to = e[x][i];
		dfs(to,x); //忘了往下递归!
		memcpy(g,h[x],sizeof(h[x])); //一定要注意递归下去的数组会改变。不能用标号h[i - 1]!!!

		h[x][1][1] = g[1][0] + f[to][1];
		h[x][1][0] = max(g[1][0],g[1][1]) + f[to][0];
		h[x][0][1] = g[0][0] + f[to][1];
		h[x][0][0] = max(g[0][0],g[0][1]) + f[to][0];
		
	}
	f[x][0] = max(h[x][0][1],h[x][0][0]);
	f[x][1] = h[x][1][0];
}
#undef e

int main(){
//	freopen("input.txt","r",stdin);
	scanf("%d %d",&n,&m);
	for (int i = 1 ; i <= n ; i++) scanf("%d",&val[i]);
	for (int i = 1 ; i <= m ; i++){
		int x,y;
		scanf("%d %d",&x,&y);
		adde(x,y);
	}
	build();
	dfs(1,0);
//	for (int i = 1;  i <= tot ; i++) cout<
	cout<<max(f[1][0],f[1][1])<<endl;
}

bzoj 2125: 最短路

仙人掌上最短路
直接圆方树
注意建树的时候处理环长的 问题。
我利用了仙人掌的性质:每条边只在一个环上,如果有返祖边,最多只有一条,并且一定成环,所以把这条边提前放入栈中。这样算环长很方面。
在圆方树上求lca要讨论是圆点还是方点,如果是方点,则表示在环上合并,要取距离较小的一段
倍增被卡常。悄悄开了O3

#include
using namespace std;

#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) ((x)&(-(x)))

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;

const ld inf = 2e18;
const int N = 3e6 + 10;
const int maxn = 30020;
const ll mod = 1e9 + 7;

vector <int> e[maxn],e2[maxn];
vector <ll> w[maxn],w2[maxn];
vector <pr> cur;

int tops,n,m,Q,dfn[maxn],low[maxn],tot,tag[maxn],val[maxn],dfstime;
pr st[maxn];

int jump[15][maxn],dth[maxn],fa[maxn];
ll len[maxn],dis[15][maxn];

void adde(int x,int y,int d){
	e[x].pb(y);
	w[x].pb(d);
	e[y].pb(x);
	w[y].pb(d);
}
void adde2(int x,int y,ll d);
void tarjian(int x,int fa){
	dfn[x] = low[x] = ++dfstime;
	for (int i = 0 ; i < e[x].size() ; i++){
		int to = e[x][i];
		if ( to == fa ) continue;
		st[++tops] = mp(to,w[x][i]);
		if ( !dfn[to] ){	
			tarjian(to,x);
			low[x] = min(low[to],low[x]);
			if ( low[to] >= dfn[x] ){
				ll tot_L = 0;
				cur.clear();
				do{
					cur.pb(st[tops]);
					tot_L += st[tops].se;
				}while ( st[tops--].fi != to );
				if ( cur.size() == 1 ){
					adde2(x,cur[0].fi,cur[0].se);
				}
				else if ( cur.size() > 1 ){
					tag[++tot] = 1 , len[tot] = tot_L;
					adde2(x,tot,0);
					ll curd = 0;
					for (int i = 0 ; i < cur.size() ; i++){
						//cout<
						len[cur[i].fi] = curd;
						if ( i ) adde2(tot,cur[i].fi,min(tot_L - curd,curd));
						curd += cur[i].se;
					}
				
					//cout<<"end circle\n";
				}
			}
		}
		else{
			//low[x] = min(dfn[to],low[x]);
			if ( dfn[to] < dfn[x] ) low[x] = dfn[to];
			else tops--;
		}
	}	
}
#define e e2
#define w w2
void adde2(int x,int y,ll d){
	//cout<
	e[x].pb(y);
	w[x].pb(d);
	e[y].pb(x);
	w[y].pb(d);
}
void build(){
	tot = n;
	for (int i = 1 ; i <= n ; i++) if ( !dfn[i] ) tarjian(i,0);
	//for (int i = 1 ; i <= n ; i++) cout<
}
void dfs(int x){
	//cout<<"dfs : "<
	for (int i = 0 ; i < e[x].size() ; i++){
		int to = e[x][i];
		if ( to == fa[x] || dth[to] ) continue;
		fa[to] = jump[0][to] = x;
		dth[to] = dth[x] + 1 , dis[0][to] = w[x][i];
		//cout<
		dfs(to);
	}
}
void init(){
//	rep(i,1,tot) cout<
//	cout<
	//for (int i = 1 ; i <= n ; i++){ rvc(j,e[i]) cout<
	dth[1] = 1 , dfs(1);
	rep(i,1,13)
		rep(j,1,tot)
			jump[i][j] = jump[i - 1][jump[i - 1][j]] , dis[i][j] = dis[i - 1][j] + dis[i - 1][jump[i - 1][j]];

//	rep(i,1,tot){ cout<<"check : "<
}
inline int lca(int &x,int &y,ll &res){
	if ( dth[x] < dth[y] ) swap(x,y);
	int d = dth[x] - dth[y];
	rep(i,0,13) if ( (1 << i) & d ) res += dis[i][x], x = jump[i][x];
	if ( x == y ) return x;
	repd(i,13,0) if ( jump[i][x] != jump[i][y] ) res += dis[i][x] + dis[i][y] , x = jump[i][x] , y = jump[i][y];
	//res += dis[0][x] + dis[0][y];
	return fa[x];
}
ll query(int x,int y){
	ll res = 0;
	int d = lca(x,y,res);
	//cout<
	if ( tag[d] ){
		res += min(abs(len[x] - len[y]) , len[d] - abs(len[x] - len[y]));
	}
	else if ( x != y ) res += dis[0][x] + dis[0][y];
	return res;
}
#undef e
#undef w

int main(){
//
	//	freopen("input.txt","r",stdin);
	scanf("%d %d %d",&n,&m,&Q);
	rep(i,1,m){
		int x,y,w;
		scanf("%d %d %d",&x,&y,&w);
		adde(x,y,w);
		//adde(y,x,w);
	}
	build();
	init();
	while ( Q-- ){
		int x,y;
		scanf("%d %d",&x,&y);
		printf("%lld\n",query(x,y));
	}
}

你可能感兴趣的:(知识点总结,图论)