数据结构 Week 3 --- dsu on tree 和 点分治

做了几道dsu on tree后再写点分治,发现点分治的题都能用dsu on tree做

然后发现这两者是有很多共同之处的

  • 都枚举了每个点为根节点
  • 都是nlogn的遍历
  • 大都记录了每个点到根节点的整条链的信息

难度基本不大

个人觉的还是dsu on tree好用一点,感觉代码量更小

点分治的一题:

P4178 Tree

计算,计入(ad),清空(clear),套个树状数组

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int MAXN = 1e5+7;
struct EDGE{
	int to,w,next;
}edge[MAXN*2];
int head[MAXN],tot = 0;
int n,m,u,v,w,k;
int ans = 0;
void add(int u,int v,int w){
	tot++;
	edge[tot].to = v;
	edge[tot].w = w;
	edge[tot].next = head[u];
	head[u] = tot;
}

int rt,all;
int siz[MAXN],maxp[MAXN];
bool vis[MAXN];
void getrt(int s,int f){
	siz[s] = 1;
	maxp[s] = 0;
	for(int i = head[s];i;i=edge[i].next){
		int po = edge[i].to;
		if(po == f || vis[po]) continue;
		getrt(po,s);
		siz[s] += siz[po];
		maxp[s] = max(maxp[s],siz[po]);
	}	
	maxp[s] = max(maxp[s],all - siz[s]);
	if(maxp[s] < maxp[rt]) rt = s;
}
int bit[MAXN];
int lowbit(int x) {return x & -x;}
void ADD(int pos,int add){
	for(int i = pos;i<=k;i+=lowbit(i)) bit[i] += add;
}
int Q(int pos){
	int res = 0;
	for(int i = pos;i;i-=lowbit(i)) res += bit[i];
	return res; 
}
void calc(int s,int f,int lc){//计算 
	ans++;//加上根节点到这个点本身 
	ans += Q(k - lc);
	for(int i = head[s];i;i=edge[i].next){
		int po = edge[i].to;
		if(po == f || vis[po]) continue;
		int w = edge[i].w;
		if(lc + w > k) continue;
		calc(po,s,lc + w);
	}	
} 
void ad(int s,int f,int lc){
	ADD(lc,1);
	for(int i = head[s];i;i=edge[i].next){
		int po = edge[i].to;
		if(po == f || vis[po]) continue;
		int w = edge[i].w;
		if(lc + w > k) continue;
		ad(po,s,lc + w);
	}
}
void clear(int s,int f,int lc){
	ADD(lc,-1);
	for(int i = head[s];i;i=edge[i].next){
		int po = edge[i].to;
		if(po == f || vis[po]) continue;
		int w = edge[i].w;
		if(lc + w > k) continue;
		clear(po,s,lc + w);
	}
}
void solve(int s){
	for(int i = head[s];i;i=edge[i].next){
		int po = edge[i].to;
		if(vis[po]) continue;
		int w = edge[i].w;
		if(w > k)continue;
		calc(po,s,w);
		ad(po,s,w);
	}
	for(int i = head[s];i;i=edge[i].next){
		int po = edge[i].to;
		if(vis[po]) continue;
		int w = edge[i].w;
		if(w > k)continue;
		clear(po,s,w);
	}
}
void divide(int s){
	vis[s] = true;
	solve(s);
	for(int i = head[s];i;i=edge[i].next){
		int po = edge[i].to;
		if(vis[po]) continue;
		maxp[rt = 0] = all = siz[po];
		getrt(po,0);
		getrt(rt,0);
		divide(rt);
	}
}
int main()
{
	cin>>n;
	for(int i = 1;i <= n - 1;i++){
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
		add(v,u,w);
	}
	cin>>k;
	maxp[rt = 0] = all = n;
	getrt(1,0);
	getrt(rt,0);
	divide(rt);
	cout<<ans<<endl;
	return 0;
}

  

dsu on tree的一题:

CF600 E. Lomsat gelral

计算和计入同时进行(是否同时进行要根据题目判断),对于轻儿子要清空

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int MAXN = 1e5+7;
int n, u, v;
int c[MAXN];
long long ans[MAXN],sum = 0;
struct EDGE{
	int to,next;
}edge[MAXN*2];
int head[MAXN],tot = 0;
void add(int u,int v){
	tot++;
	edge[tot].to = v;
	edge[tot].next = head[u];
	head[u] = tot;
}
int siz[MAXN],deep[MAXN],son[MAXN],fa[MAXN];
void dfs1(int s,int f){
	siz[s] = 1;deep[s] = deep[f] + 1,fa[s] = f;
	for(int i = head[s];i;i=edge[i].next){
		int po = edge[i].to;
		if(po == fa[s]) continue;
		dfs1(po,s);
		siz[s] +=siz[po];
		if(siz[po]>siz[son[s]]) son[s] = po; 
	}
}
int cnt[MAXN],ma = 0;
void count(int s){
	cnt[c[s]]++;
	if(cnt[c[s]] > ma){
		ma = cnt[c[s]];
		sum = c[s];
	}
	else if( cnt[c[s]] == ma) sum += c[s];
}
void calc(int s){
	count(s);
	for(int i = head[s];i;i=edge[i].next){
		int po = edge[i].to;
		if(po == fa[s]) continue;
		calc(po);
	}
}
void clear(int s){
	sum = ma = 0;
	cnt[c[s]]--;
	for(int i = head[s];i;i=edge[i].next){
		int po = edge[i].to;
		if(po == fa[s]) continue;
		clear(po);
	}
}
void DSU_ON_TREE(int s,bool is_heavy){
	for(int i = head[s];i;i=edge[i].next){
		int po = edge[i].to;
		if(po == son[s] || po == fa[s]) continue;
		DSU_ON_TREE(po,0);//求轻儿子 (计算轻儿子并清空)
	}
	if(son[s]) {
		DSU_ON_TREE(son[s],1);//求重儿子 (计算重儿子不清空)
	}
	for(int i = head[s];i;i = edge[i].next){//计算轻儿子 
		int po = edge[i].to;
		if(po == son[s] || po == fa[s]) continue;
		calc(po);
	}
	count(s);//计算根节点 
	ans[s] = sum;
	if(!is_heavy) clear(s);//不是重儿子,清空 
	return;
}
int main()
{
	cin>>n;
	for(int i = 1;i <= n;i++) scanf("%d",&c[i]),head[i] = 0;
	for(int i = 1;i <= n - 1;i++) {
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	dfs1(1,0);
	DSU_ON_TREE(1,1);
	for(int i = 1;i <= n;i++){
		printf("%lld ",ans[i]);
	}
	printf("\n");
	return 0;
}

  

 

posted @ 2021-05-29 15:01  beta_dust  阅读(66)  评论(0编辑  收藏  举报