CF161D【题解】点分治
题目链接:
CF:http://codeforces.com/contest/161/problem/D
Luogu:https://www.luogu.org/problem/CF161D
点分治的裸题。
点分治关键在于如何更新答案。
找根和分治的步骤很简单。
这道题要求树上路径刚好为K的个数。
那么就可以用小于等于K的路径个数减去小于K的路径个数。
其他的就很套路了。
代码如下:
#include<bits/stdc++.h> using namespace std; const int maxn=50010; struct node{ int nxt,to,dis; #define nxt(x) e[x].nxt #define to(x) e[x].to #define dis(x) e[x].dis }e[maxn<<1]; int head[maxn],tot,n,k; inline void add(int from,int to,int dis){ to(++tot)=to;dis(tot)=dis; nxt(tot)=head[from];head[from]=tot; } int maxp[maxn],siz[maxn],vis[maxn],sum,rt; inline void getrt(int now,int fa){ siz[now]=1;maxp[now]=0; for(int i=head[now];i;i=nxt(i)){ int to=to(i); if(vis[to]||to==fa) continue; getrt(to,now); siz[now]+=siz[to]; maxp[now]=max(maxp[now],siz[to]); } maxp[now]=max(maxp[now],sum-siz[now]); if(maxp[now]<maxp[rt]) rt=now; } int re[maxn],dep[maxn],cnt; long long ans=0; inline void getdis(int now,int fa){ re[++cnt]=dep[now]; for(int i=head[now];i;i=nxt(i)){ int to=to(i); if(to==fa||vis[to]) continue; dep[to]=dep[now]+dis(i); getdis(to,now); } } inline int calc(int now,int w){ cnt=0;dep[now]=w; getdis(now,0); sort(re+1,re+1+cnt); int l=1,r=cnt,res=0; while(l<r){ if(re[l]+re[r]<=k) res+=(r-l),l++; else r--; } l=1,r=cnt; while(l<r){ if(re[l]+re[r]<k) res-=(r-l),l++; else r--; } return res; } inline void solve(int now){ ans+=calc(now,0);vis[now]=1; for(int i=head[now];i;i=nxt(i)){ int to=to(i); if(vis[to]) continue; ans-=calc(to,dis(i)); rt=0;sum=siz[to]; getrt(to,0); solve(rt); } } int main() { scanf("%d%d",&n,&k); for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); add(u,v,1);add(v,u,1); } sum=maxp[0]=n; rt=0; getrt(1,0); solve(rt); printf("%I64d\n",ans); system("pause"); return 0; }