• BZOJ2599


    Portal

    Description

    给出一棵(nleq2 imes10^5)个点的带边权树,求所有长度等于(k(kleq10^6))的简单路径中最少的边数。

    Solution

    用类似树形DP的方法,记录(pre[i])表示前若干棵子树中所有以根(rt)为起点的长度为(i)的路径中最少的边数,初始值为(pre[0]=0),其余为(+infty)。那么当我们在当前子树中找到一个距根距离为(dst),深度为(dpt)的点时,我们就可以用(pre[k-dst]+dpt)来更新答案。用当前子树内的所有点更新完(ans)后,就将其合并到(pre)中,然后计算下一个子树的贡献。
    需要注意的是,每次进行分治前不能使用memset来初始化(pre)!!!因为这样每次分治时的复杂度都为(O(siz)),总复杂度就成了(O(ncdot siz))了。所以分治结束前要手动(DFS)一下来重置(pre)。并且在(DFS)时如果当前点的(dst)已经大于(k)的话就直接返回,后面的点既没必要做也开不了数组。

    时间复杂度(O(nlogn))

    Code

    //[IOI2011]Race
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    using std::max; using std::min;
    typedef std::pair<int,int> prInt;
    inline char gc()
    {
        static char now[1<<16],*s,*t;
        if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
        return *s++;
    }
    inline int read()
    {
        int x=0; char ch=gc();
        while(ch<'0'||'9'<ch) ch=gc();
        while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
        return x;
    }
    int const N=2e5+10;
    int const K=1e6+10;
    int const INF=0x3F3F3F3F;
    int n,k;
    int cnt,h[N];
    struct edge{int v,w,nxt;} ed[N<<1];
    void edAdd(int u,int v,int w)
    {
    	cnt++; ed[cnt].v=v,ed[cnt].w=w,ed[cnt].nxt=h[u],h[u]=cnt;
    	cnt++; ed[cnt].v=u,ed[cnt].w=w,ed[cnt].nxt=h[v],h[v]=cnt;
    }
    int ans;
    int G,siz0,siz[N],chSiz[N]; bool vst[N];
    void getG(int u,int fa)
    {
    	siz[u]=1,chSiz[u]=0;
    	for(int i=h[u];i;i=ed[i].nxt)
    	{
    		int v=ed[i].v;
    		if(vst[v]||v==fa) continue;
    		getG(v,u); siz[u]+=siz[v],chSiz[u]=max(chSiz[u],siz[v]);
    	}
    	chSiz[u]=max(chSiz[u],siz0-siz[u]);
    	if(chSiz[u]<chSiz[G]) G=u;
    }
    int tCnt; prInt t[N]; int pre[K];
    void getD(int u,int fa,int dst,int dpt)
    {
    	if(dst>k) return;
    	t[++tCnt]=prInt(dst,dpt);
    	for(int i=h[u];i;i=ed[i].nxt)
    	{
    		int v=ed[i].v;
    		if(vst[v]||v==fa) continue;
    		getD(v,u,dst+ed[i].w,dpt+1);
    	}
    }
    int calc(int u,int d0)
    {
    	tCnt=0; getD(u,0,d0,1);
    	int res=INF;
    	for(int i=1;i<=tCnt;i++) res=min(res,t[i].second+pre[k-t[i].first]);
    	for(int i=1;i<=tCnt;i++) pre[t[i].first]=min(pre[t[i].first],t[i].second);
    	return res;
    }
    void reset(int u,int fa,int dst)
    {
    	if(dst>k) return;
    	pre[dst]=INF;
    	for(int i=h[u];i;i=ed[i].nxt)
    	{
    		int v=ed[i].v;
    		if(!vst[v]&&v!=fa) reset(v,u,dst+ed[i].w);
    	}
    }
    void solve(int u);
    void DC(int u)
    {
    	vst[u]=true; pre[0]=0;
    	for(int i=h[u];i;i=ed[i].nxt)
    	{
    		int v=ed[i].v;
    		if(vst[v]) continue;
    		if(siz[v]>siz[u]) siz[v]=siz0-siz[u];
    		ans=min(ans,calc(v,ed[i].w));
    	}
    	reset(u,0,0);
    	for(int i=h[u];i;i=ed[i].nxt) {int v=ed[i].v; if(!vst[v]) solve(v);}
    }
    void solve(int u) {siz0=siz[u],G=0,chSiz[G]=n,getG(u,0),DC(G);}
    int main()
    {
    	freopen("bz2599.in","r",stdin);
    	n=read(),k=read();
    	for(int i=1;i<=n-1;i++)
    	{
    		int u=read()+1,v=read()+1,w=read();
    		edAdd(u,v,w);
    	}
    	memset(pre,0x3F,sizeof pre);
    	ans=INF; siz[1]=n,solve(1);
    	if(ans<INF) printf("%d
    ",ans);
    	else puts("-1");
    	return 0;
    }
    

    P.S.

    不只是点分治,CDQ分治时也不能使用memset来初始化。我感觉大部分的分治似乎都不行呢。

  • 相关阅读:
    Power Apps 创建响应式布局
    SharePoint Online 软件边界限制
    Power Apps 中人员选择器的使用
    Power Apps 中修改 SharePoint Online 数据
    Power Apps 中调用 Automate 工作流
    如何查看你的Office 365 账号的订阅
    Microsoft Teams 中嵌入SharePoint Online 页面
    SharePoint Online 触发Outlook邮件内审批
    Linux查看实时网卡流量的几种方式
    Linux性能优化和监控系列(三)——分析Memory使用状况
  • 原文地址:https://www.cnblogs.com/VisJiao/p/BZOJ2599.html
Copyright © 2020-2023  润新知