[POJ 1741] Tree
Posted newera
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[POJ 1741] Tree相关的知识,希望对你有一定的参考价值。
Link:
Solution:
此题的难点在于点分治上的统计
如果依然采取每棵子树的结果与之前所有子树的结果合并的方式会比较麻烦
同时复杂度可能超过$O(n*log(n))$的限制
其实可以用一个简单的容斥来在$O(n*log(n))$统计结果:
先将所有点到重心的距离排序,维护左右指针来直接统计结果
但这样明显会重复计算两个点在同一棵子树中的情况,按照同样的操作将其除去即可
Code:
//by NewErA #include <iostream> #include <cstdio> #include <cstdlib> #include <queue> #include <vector> #include <cstring> #include <algorithm> using namespace std; typedef pair<int,int> P; const int MAXN=10005; vector<P> G[MAXN];vector<int> tmp; int n,k,mx_sub[MAXN],sz[MAXN],vis[MAXN],v_sum=0,root,res=0; int dist[MAXN]; void getroot(int x,int anc) { sz[x]=1;mx_sub[x]=0; for(int i=0;i<G[x].size();i++) { int v=G[x][i].first; if(v==anc || vis[v]) continue; getroot(v,x);sz[x]+=sz[v]; mx_sub[x]=max(mx_sub[x],sz[v]); } mx_sub[x]=max(mx_sub[x],v_sum-sz[x]); if(mx_sub[x]<mx_sub[root]) root=x; } void cal_dep(int x,int anc) { tmp.push_back(dist[x]); for(int i=0;i<G[x].size();i++) { int v=G[x][i].first; if(v==anc || vis[v]) continue; dist[v]=dist[x]+G[x][i].second; cal_dep(v,x); } } int cal(int x,int start_point) { tmp.clear();dist[x]=start_point; cal_dep(x,0);sort(tmp.begin(),tmp.end()); int ret=0,l=0,r=tmp.size()-1; while(l<r) { if(tmp[l]+tmp[r]<=k) ret+=(r-l),l++; else r--; } return ret; } void solve(int x) { res+=cal(x,0);vis[x]=true; for(int i=0;i<G[x].size();i++) { int v=G[x][i].first; if(vis[v]) continue; res-=cal(v,G[x][i].second); v_sum=sz[v];getroot(v,root=0); solve(root); } } int main() { while(scanf("%d%d",&n,&k)!=EOF && n && k) { for(int i=0;i<=n;i++) G[i].clear(),vis[i]=mx_sub[i]=0; for(int i=1;i<n;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); G[x].push_back(P(y,z));G[y].push_back(P(x,z)); } mx_sub[0]=v_sum=n;getroot(1,root=0); res=0;solve(root); printf("%d ",res); } return 0; }
Review:
点分治中可用容斥的思想来统计
先计算任意两点的结果,再减去两点在同一棵子树中的结果(重心转移后会重复计算)
以上是关于[POJ 1741] Tree的主要内容,如果未能解决你的问题,请参考以下文章