zoukankan      html  css  js  c++  java
  • 快递员 点分治

    快递员

    LG传送门

    点分治好题。然而似乎不是一般意义上的点分治?

    首先要看清楚题,我就被题意卡了,这道题是在一棵树上给出(m)个点对,求树上一个点,使得对于其中任意一个点对,这个点到那两个点的距离和的最大值最小。乍一看还以为要二分,发现二分做不了。

    先想(nm)暴力,需要发现一个性质。考虑在树上取点,取什么点的时候可以确定答案不会更优?考虑当前取到的点(r)出发dfs求出树上所有点到它的距离,找出能使距离和最大的(q)个点对:假如在这些点对中存在一个点对,满足点对中的两个点分别在(r)的不同子树中,那么我们就找到了答案,因为无论(r)取在任何地方,它到这个点对的两个点的距离和一定不会更短(可以手玩);假如存在两个点对,满足他们分别在(r)的不同子树中,那么我们也找到了答案,因为无论(r)怎样移动,这两个点对中至少有一个到(r)的距离和会增加。

    这样我们就得到了复杂度(O(nm))的算法:先在树上任取一个点,然后递归执行以下过程,从这个点出发求到树上每个点的距离,在(m)取出个点对中取出能使距离和最大的点对,对他们执行在上面提到的两种判断,如果满足就输出,不满足就把所选取的当前点往那些点对所在的子树中挪动(那些点对只可能在同一棵子树内),如果最后无处可挪也说明找到了答案。

    但是这样的算法仍然不够优秀,我们有(O(mlogn))的算法:把每次挪动改为跳到目标子树的重心,就降下了复杂度。事实上,这样做与一般的点分治有些不同只是,借用了点分治的思想,反倒有点类似于在序列上二分(可以这样理解,当树变成一条链的时候也的确是这样)。

    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #define R register
    #define I inline
    using namespace std;
    const int S=100003,N=200003,inf=0x7fffffff;
    char buf[1000000],*p1,*p2;
    I char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;}
    I int rd(){
    	R int f=0; R char c=gc();
    	while(c<48||c>57) c=gc();
    	while(c>47&&c<58) f=f*10+(c^48),c=gc();
    	return f;
    }
    int h[S],s[N],g[N],w[N],t[S],v[S],a[S],b[S],k[S],p[S],q[S],c,n,m,u,r,o;
    I int min(int x,int y){return x<y?x:y;}
    I int max(int x,int y){return x>y?x:y;}
    I void add(int x,int y,int z){s[++c]=h[x],h[x]=c,g[c]=y,w[c]=z;}
    void gts(int x,int f){t[x]=1;
    	for(R int i=h[x],y;i;i=s[i]) if(!v[y=g[i]]&&y^f) gts(y,x),t[x]+=t[y];
    }
    void gtr(int x,int f,int a){R int i,y,m=0;
    	for(i=h[x];i;i=s[i]) if(!v[y=g[i]]&&y^f) gtr(y,x,a),m=max(m,t[y]);
    	m=max(m,a-t[x]); if(m<u) u=m,r=x;
    }
    void dfs(int x,int f,int d,int u){p[x]=u,k[x]=d;
    	for(R int i=h[x],y;i;i=s[i]) if((y=g[i])^f) dfs(y,x,d+w[i],u);
    }
    void dac(int x){
    	u=n,gts(x,0),gtr(x,0,t[x]);
    	if(v[r]) printf("%d",o),exit(0);
    	v[r]=1,k[r]=0;
    	R int i,u=0;
    	for(i=h[r];i;i=s[i]) dfs(g[i],r,w[i],g[i]);
    	for(i=1;i<=m;++i)
    		if(k[a[i]]+k[b[i]]>u) u=k[a[i]]+k[b[i]],q[q[0]=1]=i;
    		else if(k[a[i]]+k[b[i]]==u) q[++q[0]]=i;
    	for(o=min(o,u),u=0,i=1;i<=q[0];++i){
    		if(p[a[q[i]]]^p[b[q[i]]]) printf("%d",o),exit(0);
    		else if(!u) u=p[a[q[i]]];
    		else if(u^p[a[q[i]]]) printf("%d",o),exit(0);
    	}
    	dac(u);
    }
    int main(){
    	R int i,x,y,z;
    	for(n=rd(),m=rd(),i=1;i<n;++i) x=rd(),y=rd(),z=rd(),add(x,y,z),add(y,x,z);
    	for(i=1;i<=m;++i) a[i]=rd(),b[i]=rd();
    	o=inf,dac(1);
    	return 0;
    }
    
    

    注意求距离的时候把到自己的距离更新为(0),否则在洛谷上会WA最后一个点。

  • 相关阅读:
    Win32中安全的子类化(翻译)
    OJ题目JAVA与C运行效率对比
    关协同过滤
    Objective-C ,ios,iphone开发基础:使用第三方库FMDB连接sqlite3 数据库,实现简单的登录
    GDI+简单现实文字旋转
    opencv 2.46与visual studio 2012 配置方法
    Emacs助力PowerShell
    ARC forbids explicit message send of 'autorelease'错误
    Event处理
    复制中发布服务器和订阅服务器内容不一致的解决办法
  • 原文地址:https://www.cnblogs.com/cj-chd/p/10109606.html
Copyright © 2011-2022 走看看