#173. 蚯蚓健身操
2016-12-25 21:44
316 查看
题意:给定一颗树(n<=100000),每次询问(q<=200)最大的连通块,满足其中任意点分别到离它最远的叶子结点的距离差的绝对值最大不超过l。
先三次DFS找出离每个点最远的叶子结点的距离。
方法:先DFS出直径,再分别从直径的两端开始DFS,将两次得到的D取max即可。
(证明:假设结论不成立,即存在一个点x,到另一个非直径端点y的距离大于到两个直径端点的距离,画图发现这与直径定义相悖。然后就假装我证好了。)
按D的大小对点进行sort,发现它的逆序就是一个和拓扑序很像的东西。按照这个顺序,不停地计算以该点D为最小值的最大连通块(用并查集维护,不符合要求的点令其sz--,然后放到其父亲所在并查集中)即可。
注意不能直接挂父节点上,因为假设一点x,父亲为fa,par[x]==x,而par[fa]不一定为fa,因为考虑一条链(当然不一定是),D[fa]很可能大于D[x],所以如果直接用fa计算会导致sz出问题,所以要用par[fa]算。讲道理我好像因此调了一个晚上。(其实平安哥已经告诉我了,但我没听出来他说的是这个意思,然后他也以为我处理过了,然后就biubiu……)
不多说了,挂代码。
先三次DFS找出离每个点最远的叶子结点的距离。
方法:先DFS出直径,再分别从直径的两端开始DFS,将两次得到的D取max即可。
(证明:假设结论不成立,即存在一个点x,到另一个非直径端点y的距离大于到两个直径端点的距离,画图发现这与直径定义相悖。然后就假装我证好了。)
按D的大小对点进行sort,发现它的逆序就是一个和拓扑序很像的东西。按照这个顺序,不停地计算以该点D为最小值的最大连通块(用并查集维护,不符合要求的点令其sz--,然后放到其父亲所在并查集中)即可。
注意不能直接挂父节点上,因为假设一点x,父亲为fa,par[x]==x,而par[fa]不一定为fa,因为考虑一条链(当然不一定是),D[fa]很可能大于D[x],所以如果直接用fa计算会导致sz出问题,所以要用par[fa]算。讲道理我好像因此调了一个晚上。(其实平安哥已经告诉我了,但我没听出来他说的是这个意思,然后他也以为我处理过了,然后就biubiu……)
不多说了,挂代码。
#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> using namespace std; #define rep(i,j,k) for(i=j;i<=k;++i) #define per(i,j,k) for(i=j;i>=k;--i) #define ll long long #define pli pair<ll,int> #define mkp make_pair #define X first #define Y second #define N 1000005 ll n,m,D ,pos ,Max ,ans; bool cmp(ll x,ll y){ return D[x]<D[y]; } struct POINT{ll K,W,ne;}P ;ll he ,tot; void add(ll x,ll y,ll z){ P[++tot]=(POINT){y,z,he[x]};he[x]=tot; } ll d[4] ,rt[4],pre ; void DFS(ll x,ll e,ll k){ ll p,y; for(p=he[x];p;p=P[p].ne)if(p!=e){ d[k][y=P[p].K]=d[k][x]+P[p].W; if(d[k][y]>d[k][rt[k]])rt[k]=y; DFS(y,p^1,k); } } void DFS2(ll x,ll e){ ll p,y; for(p=he[x];p;p=P[p].ne)if(p!=e){ pre[y=P[p].K]=x;DFS2(y,p^1); } } ll par ,sz ; ll getpar(ll x){ if(par[x]!=x)par[x]=getpar(par[x]); return par[x]; } int main(){ ll x,y,z,l,i; scanf("%lld",&n);tot=1; rep(i,2,n){ scanf("%lld%lld%lld",&x,&y,&z); add(x,y,z);add(y,x,z); } rt[1]=1;DFS(1,0,1); rt[2]=rt[1];DFS(rt[1],0,2); rt[3]=rt[2];DFS(rt[2],0,3); rep(i,1,n)D[pos[i]=i]=max(d[2][i],d[3][i]); sort(pos+1,pos+n+1,cmp); DFS2(pos[1],0); scanf("%lld",&m); while(m--){ scanf("%lld",&l); rep(i,1,n)sz[par[i]=i]=1; x=n;ans=0; per(i,n,1){ y=pos[i]; while(D[pos[x]]>l+D[y])--sz[getpar(pos[x--])]; z=getpar(pre[y]); ans=max(sz[y],ans); if(y!=z) sz[par[y]=z]+=sz[y]; } printf("%lld\n",ans); } return 0; }
相关文章推荐
- Ubuntu Server配置apt-get源的方法
- NOI2014-魔法森林(LCT)
- 十二月英语总结--充满热情
- spring boot cloud热部署插件简单配置
- 去掉不用的工作空间
- 20145317《信息安全系统设计基础》课程总结
- 28. Implement strStr()*
- 程序题——清零和设置bit位
- Quartz新特性
- jdis操作redis cluster
- 时间介词
- leetcode-- Ugly Number
- 希望有兴趣的加入,共同为项目智能化管理jar包而努力 第二篇
- C wait()和waitpid()
- maven
- 不使用循环,判断一个数是否是2的N次方
- ICA
- 【Dongle】【Java】简介
- javac找不到或无法加载主类 com.sun.tools.javac.Main,
- 使用webpack打包ThinkPHP的资源文件