zoukankan      html  css  js  c++  java
  • 【CF516D】Drazil and Morning Exercise(换根DP预处理+暴力双指针)

    点此看题面

    • 给定一棵(n)个点的有根树,每条边有一个边权。
    • 定义(f(x)=max_{i=1}^ndist(x,i))
    • (q)组询问,每次给出一个数(l),求最大的满足(max_{xin S}f(x)-min_{xin S}f(x)le l)的连通块(S)的大小。
    • (nle10^5,qle50)

    换根(DP)预处理

    首先我们要预处理所有(f(x))

    这应该是一个非常简单的换根(DP),首先第一次(dfs)预处理出每个点的最长链、次长链,然后第二次(dfs)记录一个子树外的最长路径再扫一遍即可。

    暴力双指针

    发现(q)非常小,因此考虑对于每次询问直接上双指针跑一遍。

    我们从大到小枚举左端点,移动右端点以满足最大值减最小值不超过(l),然后就是要询问左端点所在连通块的大小。

    看起来点既会加入又会删去,貌似要写个(LCT)。(当然想写(LCT)也没关系啦)

    不过我们考虑这题中(f(x))的实际意义,其实(f(x))越大的点就离重心越远。

    而我们每次删掉的是(f(x))最大的点,它必然在边缘处,故该点是否存在并不影响现在及以后任一时刻的连通性,只要把所在连通块大小减(1)即可。

    代码:(O(nlogn+nqalpha(n)))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define LL long long
    #define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z)
    using namespace std;
    int n,p[N+5],Ex[N+5],ee,lnk[N+5];struct edge {int to,nxt,v;}e[N<<1];
    int fa[N+5],g[N+5];I int getfa(CI x) {return fa[x]^x?fa[x]=getfa(fa[x]):x;}
    namespace TreeDP//换根DP
    {
    	LL Mx[N+5],Sx[N+5];int P[N+5];I void dfs1(CI x,CI lst=0)//第一遍dfs预处理最长链次长链
    	{
    		LL t;for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&(dfs1(e[i].to,x),
    			(t=Mx[e[i].to]+e[i].v)>Mx[x]?(Sx[x]=Mx[x],Mx[x]=t,P[x]=e[i].to):Sx[x]<t&&(Sx[x]=t));
    	}
    	LL f[N+5];I void dfs2(CI x,CI lst=0,Con LL& M=0)//第二遍dfs换根DP求出f(x)
    	{
    		f[x]=max(Mx[x],M);for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&
    			(dfs2(e[i].to,x,max(M,P[x]^e[i].to?Mx[x]:Sx[x])+e[i].v),0);
    	}
    }
    using namespace TreeDP;
    I bool cmp(CI x,CI y) {return f[x]<f[y];}//根据f(x)大小排序
    int main()
    {
    	RI Qt,i,x,y,z;for(scanf("%d",&n),i=1;i^n;++i) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
    	LL t;for(dfs1(1),dfs2(1),i=1;i<=n;++i) p[i]=i;sort(p+1,p+n+1,cmp);
    	RI ans;scanf("%d",&Qt);W(Qt--)
    	{
    		for(scanf("%lld",&t),i=1;i<=n;++i) fa[i]=i,g[i]=Ex[i]=0;//清空
    		for(ans=0,x=y=n;y;ans=max(ans,g[p[y]]),--y)//从大到小枚举左端点
    		{
    			W(f[p[x]]-f[p[y]]>t) --g[getfa(p[x])],Ex[p[x--]]=0;g[p[y]]=Ex[p[y]]=1;//双指针,维护右端点
    			for(i=lnk[p[y]];i;i=e[i].nxt) Ex[e[i].to]&&(fa[z=getfa(e[i].to)]=p[y],g[p[y]]+=g[z]);//更新连通性
    		}printf("%d
    ",ans);
    	}return 0;
    }
    
  • 相关阅读:
    网络流与线性规划24题 之 餐巾计划问题
    待刷题目分类(一)
    bzoj1787 [Ahoi2008]Meet 紧急集合
    Hoj2634 How to earn more?
    poj3281 Dining
    浅谈数论(三)水仙花数
    poj1637 Sightseeing tour
    动态规划的思考(1)
    网络流-最大流问题 ISAP 算法解释(转自Renfei Song's Blog)
    poj1273 Drainage Ditches
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF516D.html
Copyright © 2011-2022 走看看