zoukankan      html  css  js  c++  java
  • Jzoj4715 树上路径

    给出一棵树,求出最小的k,使得,且在树中存在路径p,使得k>=S且k<=E。(k为路径p上的边的权值和)

    这是一道点分治的题目,不过做法多样,也可以用启发式合并水过去,好像还有一种方法是什么二分

    如果直接点分治的话,我们将子树内所有的点排序,求出其所处的子树编号belong,再求出next数组,next[i]=j表示belong[i]!=belong[j]的最小的j(i<j),这样的话,我们对于一个i就可以二分答案求出一个j使得v[i]+v[j]>=S的j,若belong[i]=belong[j]则令j=next[j],统计答案即可

    #pragma GCC opitmize("O3")
    #pragma G++ opitmize("O3")
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #define N 100010
    using namespace std;
    struct Node{ int len,p; }s[N];
    struct Edge{ int v,c,nt; } G[N<<1];
    int d[N],sz[N],f[N]={1<<27},vis[N],h[N];
    int pnt[N],n,rt,cnt=0,t,nS,nt[N],S,E,A=1<<30;
    inline void gmin(int& x,int y){ if(x>y)x=y; }
    inline void gmax(int& x,int y){ if(x<y)x=y; }
    inline bool c1(Node a,Node b){ return a.len<b.len; }
    inline void adj(int x,int y,int c){ 
    	G[++cnt]=(Edge){y,c,h[x]}; h[x]=cnt; 
    }
    void gSize(int x,int p){
    	sz[x]=1;
    	for(int v,i=h[x];i;i=G[i].nt)
    		if(!vis[v=G[i].v] && v!=p){
    			gSize(v,x);
    			sz[x]+=sz[v];
    		}
    }
    void gRoot(int x,int p){
    	sz[x]=1; f[x]=0;
    	for(int v,i=h[x];i;i=G[i].nt)
    		if(!vis[v=G[i].v] && v!=p){
    			gRoot(v,x);
    			sz[x]+=sz[v];
    			gmax(f[x],sz[v]);
    		}
    	gmax(f[x],nS-sz[x]);
    	if(f[x]<f[rt]) rt=x;
    }
    void gParent(int x,int p){
    	s[++t]=(Node){d[x],pnt[x]};
    	for(int v,i=h[x];i;i=G[i].nt)
    		if(!vis[v=G[i].v] && v!=p){
    			if(p) pnt[v]=pnt[x];
    			d[v]=d[x]+G[i].c;
    			gParent(v,x);
    		}
    }
    void gCal(int x){
    	d[x]=0; t=0; pnt[x]=x;
    	for(int i=h[x];i;i=G[i].nt) pnt[G[i].v]=G[i].v;
    	gParent(x,0); sort(s+1,s+1+t,c1); nt[t]=t+1;
    	for(int i=t-1;i;--i) nt[i]=(s[i].p!=s[i+1].p?i+1:nt[i+1]);
    	for(int l=1,r=t;l<r;++l){
    		for(;l<r&&s[r-1].len+s[l].len>=S;--r);
    		if(s[l].p==s[r].p) r=nt[r];
    		if(r<=t&&s[l].len+s[r].len>=S) gmin(A,s[l].len+s[r].len);
    	}
    }
    void gDfs(int x){
    	gCal(x); vis[x]=1; gSize(x,0);
    	for(int v,i=h[x];i;i=G[i].nt)
    		if(!vis[v=G[i].v]){
    			rt=0; nS=sz[x];
    			gRoot(v,x); gDfs(rt);
    		}
    }
    int main(){
    	scanf("%d%d%d",&n,&S,&E);
    	for(int a,b,c,i=1;i<n;++i){
    		scanf("%d%d%d",&a,&b,&c);
    		adj(a,b,c); adj(b,a,c);
    	}
    	nS=n; gRoot(1,0); gDfs(rt);
    	if(A>E) puts("-1"); else printf("%d
    ",A);
    }
    另一种方法是启发式合并,随便选一个根,将每个子树的节点加入数组中让后用归并排序合并,合并前维护一下统计答案

    这样好像无论是菊花图还是一条链都是n^2的(雾),但是居然跑得很快

    Code来自chengziqi学(ju)长(shen):

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    const LL maxn=2e5+10;
    #define INF 1e16
    struct edge {LL t,w,next;}e[2*maxn];
    LL last[maxn],size[maxn],deep[maxn],f[maxn],g[maxn];
    LL n,m,num,cnt,total,root,left,sum,right,h[maxn];
    bool vis[maxn];
    LL read()
    {
    	LL x=0,f=1; char ch=getchar();
    	while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    	while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    	return x*f;
    }
    void write(LL x)
    {
    	if (x<0) putchar('-'),x=-x;
    	if (x>9) write(x/10);
    	putchar(x%10+'0');
    }
    void road(LL x,LL y,LL z) {e[++num].t=y; e[num].w=z; e[num].next=last[x]; last[x]=num;}
    void get_root(LL x,LL fa)
    {
    	size[x]=1; f[x]=0;
    	for (int i=last[x];i;i=e[i].next)
    	{
    		LL y=e[i].t;
    		if (vis[y]==true || y==fa)
    			continue;
    		get_root(y,x);
    		size[x]+=size[y];
    		f[x]=max(f[x],size[y]);
    	}
    	f[x]=max(f[x],total-size[x]);
    	if (f[x]<f[root]) root=x; 
    }
    void merge_sort(LL l,LL r,LL mid)
    {
    	LL s=l,t=mid+1,k=l-1;
    	for (int i=l;i<=r;i++) h[i]=g[i];
    	while (s<=mid && t<=r)
    		if (h[s]<h[t]) g[++k]=h[s++];
    		else g[++k]=h[t++];
    	while (s<=mid) g[++k]=h[s++];
    	while (t<=r) g[++k]=h[t++];
    }
    void work(LL x,LL fa)
    {
    	g[++cnt]=0; LL k=cnt+1,t=0;
    	for (int i=last[x];i;i=e[i].next)
    	{
    		LL y=e[i].t;
    		if (y==fa) continue;
    		t=cnt+1; work(y,x);
    		for (int j=t;j<=cnt;j++)
    		{
    			g[j]+=e[i].w;
    			if (g[j]>=left && g[j]<=right)
    				sum=min(sum,g[j]);
    			if (g[j]>right)
    			{cnt=j-1; break;}
    		}
    		LL s=cnt;
    		if (s<t) continue;
    		for (int j=k;j<t;j++)
    		{
    			while (g[j]+g[s]>=left && s>t) s--;
    			if (g[j]+g[s]<left && s<cnt) s++;
    			if (g[j]+g[s]>=left)
    				sum=min(sum,g[j]+g[s]);
    			if (s==t && h[s]+h[j]>=left) break;
    		}
    		if (k<t) merge_sort(k,cnt,t-1);
    	}
    }
    int main()
    {
    	root=sum=num=cnt=0; f[0]=INF;
    	memset(vis,false,sizeof(vis));
    	n=read(); left=read(); right=read();
    	for (int i=1;i<n;i++)
    	{
    		LL x=read(),y=read(),z=read();
    		road(x,y,z); road(y,x,z);
    	}
    	total=n; get_root(1,0);
    	sum=INF; work(root,0);
    	if (sum==INF) write(-1);
    	else write(sum);
    	putchar('
    '); return 0;
    }
    至于题解里面说的二分我没有看懂,可以参考其他神犇的博客

  • 相关阅读:
    npm version patch
    nginx 操作
    基于 Vue CLI 组件库封装,按需加载实现
    nginx 配置文件路径获取
    Laravel 生产资源路由并指定模型
    base.js,通用js方法,Js方法封装
    jquery.params.js,Jquery获取页面参数,js获取页面参数
    layui使用,LayUI select不显示,LayUI文件上传,Layui自定义校验规则
    Layer弹窗消息封装,Layer消息提示封装,Layer使用
    Html跨域js封装,前端页面跨域js,postMessage实现跨域
  • 原文地址:https://www.cnblogs.com/Extended-Ash/p/9477307.html
Copyright © 2011-2022 走看看