数据结构 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;
}

浙公网安备 33010602011771号