洛谷P4178 Tree
作者:互联网
考虑点分治,对于一个子树,求出每个点到根的距离并排序,用双指针扫描,但这样会把来自同一个儿子的路径也统计上去,再对每个儿子做一遍,减去即可。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; typedef long long ll; const int N=40005; char rB[1<<21],*S,*T; inline char gc(){return S==T&&(T=(S=rB)+fread(rB,1,1<<21,stdin),S==T)?EOF:*S++;} inline int rd(){ char c=gc(); while(c<48||c>57)c=gc(); int x=c&15; for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15); return x; } int G[N],to[N<<1],w[N<<1],nxt[N<<1],cnt=0,sz[N],f[N],sum,rt,d[N],t[N],tot,k; ll ans=0ll; bool vis[N],inq[N]; queue<int> Q; inline void add(int u,int v,int c){ to[++cnt]=v;w[cnt]=c;nxt[cnt]=G[u];G[u]=cnt; to[++cnt]=u;w[cnt]=c;nxt[cnt]=G[v];G[v]=cnt; } void getrt(int u,int fa){ //点分治搜重心 int i,v; sz[u]=1;f[u]=0; for(i=G[u];i;i=nxt[i])if(!vis[v=to[i]]&&v!=fa){ getrt(v,u); sz[u]+=sz[v]; f[u]=max(f[u],sz[v]); } f[u]=max(f[u],sum-sz[u]); if(f[u]<f[rt])rt=u; } inline ll calc(int u,int k){ int i,h,v,l,r; ll ans=0ll; memset(inq,0,sizeof(inq)); inq[u]=1;t[tot=1]=d[u]=0;Q.push(u); while(!Q.empty()){ h=Q.front();Q.pop(); for(i=G[h];i;i=nxt[i])if(!vis[v=to[i]]&&!inq[v]&&(d[v]=d[h]+w[i])<=k){ //大于k的距离对答案无贡献,不用保存 t[++tot]=d[v]; inq[v]=1; Q.push(v); } } sort(t+1,t+tot+1); for(l=1,r=tot;l<=r;++l){ while(l<=r&&t[l]+t[r]>k)--r; if(l>r)break; ans+=r-l; } return ans; } void solve(int u){ int i,v; vis[u]=1;ans+=calc(u,k); for(i=G[u];i;i=nxt[i])if(!vis[v=to[i]]){ ans-=calc(v,k-(w[i]<<1)); sum=sz[v];rt=0;getrt(v,u); solve(rt); } } int main(){ int n=f[0]=sum=rd(),i,u,v,c; for(i=1;i<n;++i){ u=rd();v=rd();c=rd(); add(u,v,c); } k=rd(); getrt(1,0);solve(rt); printf("%lld",ans); return 0; }View Code
标签:sz,cnt,洛谷,nxt,int,Tree,ans,include,P4178 来源: https://www.cnblogs.com/sunshine-chen/p/11258752.html