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