zoukankan      html  css  js  c++  java
  • 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来初始化。我感觉大部分的分治似乎都不行呢。

  • 相关阅读:
    How to convert VirtualBox vdi to KVM qcow2
    (OK)(OK) adb -s emulator-5554 shell
    (OK)(OK) using adb with a NAT'ed VM
    (OK) How to access a NAT guest from host with VirtualBox
    (OK) Creating manually one VMs from an existing VDI file in CLI (VBoxManage) in Fedora 23
    (OK)(OK) Creating VMs from an existing VDI file in CLI (VBoxManage) in Fedora 23
    (OK) Creating_VMs_from_an_existing_VDI_file.txt
    (OK) Creating VMs from an existing VDI file —— in OS X
    (OK) install_IBM_SERVER.txt
    (OK) install chrome & busybox in android-x86_64 —— uninstall chrome
  • 原文地址:https://www.cnblogs.com/VisJiao/p/BZOJ2599.html
Copyright © 2011-2022 走看看