CERC 17 J - Justified Jungle

传送门

题意

时限6s, 给你一颗\(n \leq 1e6\)的树,输出所有的\(i\), 使得该树可以删除某\(i\)条边,使得删除后所有的连通块大小相等

题解

虽然有结论,但还是讲讲我的做法把, 或许有所启发
考虑将枚举删除边数转换为枚举连通块大小, 不妨设每个连通块大小为\(k\)

每次从树上删除一个大小恰为\(k\)的子树,如果这样可以删除整棵树,那么就可以,否则不行,正确性应该比较正确?

如何实现呢? 如果我们能够在较小的复杂度实现发现并删除一颗大小为\(k\)的子树,那么总复杂度就是调和级数级别的

考虑这样做: 我们预处理子树大小, 把每一个点按子树大小丢入一个桶中, 对于每一个\(k\), 我们从小到大扫描它的倍数, 用线段树加dfs序维护删除后的子树大小, 一开始把所有\(k\)大小的子树删除了,然后对于所有\(2k\)桶中的子树,他们当前的大小如果是\(k\)就删除,如果不是\(k\)就无解。

总复杂度\(O(nlog^2n)\)

实现

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#define ls(x) (x*2)
#define rs(x) (x*2+1) 
#define ll long long 
using namespace std;

int read(){
	int num=0, flag=1; char c=getchar();
	while(!isdigit(c) && c!='-') c=getchar();
	if(c == '-') flag=-1, c=getchar();
	while(isdigit(c)) num=num*10+c-'0', c=getchar();
	return num*flag;
}

const int N = 1e6+10000;
int n;
vector<int> p[N];
int fa[N], dis[N], son[N], dfn[N], efn[N], cnt=0;

void dfs(int x){
	son[x] = 1;
	dfn[x] = ++cnt;
	for(auto nex : p[x]){
		if(nex == fa[x]) continue;
		fa[nex] = x;
		dis[nex] = dis[x] + 1;
		dfs(nex);
		son[x] += son[nex];
	}
	efn[x] = cnt;
}

struct Tree{
	int tree[N<<2];
	
	void push_up(int o){
		tree[o] = tree[ls(o)] + tree[rs(o)];
	}
	
	void build(int o, int l, int r){
		if(l == r){
			tree[o] = 1;
			return ;
		}
		
		int mid =(l+r)/2;
		if(tree[ls(o)] != (mid-l+1)) build(ls(o), l, mid);
		if(tree[rs(o)] != (r-mid)) build(rs(o), mid+1, r);
		push_up(o);
	}
	
	int query(int o, int l, int r, int ql, int qr){
		if(ql<=l && r<=qr) return tree[o];
		int mid=(l+r)/2, res=0;
		
		if(ql<=mid) res+=query(ls(o),l,mid, ql,qr);
		if(qr>mid) res+=query(rs(o), mid+1, r, ql,qr); 
		return res;
	} 
	
	void update(int o, int l, int r, int x, int v){
		if(l == r){
			tree[o] += v;
			return ;
		}
		
		int mid = (l+r)/2;
		if(x <= mid) update(ls(o),l,mid, x,v);
		else update(rs(o),mid+1,r, x,v);
		push_up(o);
	}
}t;
vector<int> s[N];


int check(int k){
	t.build(1,1,n);
	for(int i=1; i*k<=n; i++){
		for(auto x : s[i*k]){
			if(t.query(1,1,n,dfn[x],efn[x]) != k) return 0;
			t.update(1,1,n,dfn[x],-k);
		}
	}
	return 1;
}



int main(){
	n = read();
	for(int i=1; i<=n-1; i++){
		int u=read(), v=read();
		p[u].push_back(v);
		p[v].push_back(u);
	}
	
	dis[1] = 1;
	dfs(1);
	
	for(int i=1; i<=n; i++){
		s[son[i]].push_back(i);
	}
	
	for(int i=n-1; i>=1; i--){
		if(n % i) continue;
		if(check(i)){
			printf("%d ", n/i-1);
		}
	} 
	return 0;
} 









posted @ 2024-08-02 14:11  ltdJcoder  阅读(7)  评论(0编辑  收藏  举报