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;
}
至于题解里面说的二分我没有看懂,可以参考其他神犇的博客

原文地址:https://www.cnblogs.com/Extended-Ash/p/9477307.html