bzoj4009 [HNOI2015]接水果 整体二分+扫描线+树状数组

29 篇文章 0 订阅
12 篇文章 0 订阅

Description


给定一棵n个节点的树,m条带权树上路径(x,y,w),q个询问,求包含给定路径(a,b)的带权路径中权值第k小路径的权值
N,P,Q<=40000。

Solution


现在看啥都是病句了,病句学起来好毒啊

考虑单次询问怎么做。按照dfs序我们把一条路径视作二维平面上的一个点,把盘子能覆盖的路径视作一个或两个矩形,二分答案扫描线做矩形覆盖就行了。
现在多了多组询问,那就整体二分然后就没了

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
#define lowbit(x) (x&-x)

const int N=50005;

struct edge {int y,next;} e[N*2];
struct squr {int x1,y1,x2,y2,x3,y3,w;} t[N];
struct Q {int x,y,k,id;} q[N];
struct line {int x,l,r,v;} ;

int pos[N],dep[N],fa[N],size[N],bl[N];
int ls[N],ans[N],c[N],edCnt;

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void add_edge(int x,int y) {
	e[++edCnt]=(edge) {y,ls[x]}; ls[x]=edCnt;
	e[++edCnt]=(edge) {x,ls[y]}; ls[y]=edCnt;
}

void dfs1(int now) {
	size[now]=1;
	for (int i=ls[now];i;i=e[i].next) {
		if (e[i].y==fa[now]) continue;
		fa[e[i].y]=now; dep[e[i].y]=dep[now]+1;
		dfs1(e[i].y); size[now]+=size[e[i].y];
	}
}

void dfs2(int now,int up) {
	bl[now]=up; pos[now]=++pos[0];
	int mx=0;
	for (int i=ls[now];i;i=e[i].next) {
		if (e[i].y!=fa[now]&&size[e[i].y]>size[mx]) mx=e[i].y;
	}
	if (!mx) return ;
	dfs2(mx,up);
	for (int i=ls[now];i;i=e[i].next) {
		if (e[i].y!=fa[now]&&e[i].y!=mx) dfs2(e[i].y,e[i].y);
	}
}

int get(int x) {
	int res=0;
	for (;x;x-=lowbit(x)) res+=c[x];
	return res;
}

void add(int x,int v) {
	for (;x<=pos[0];x+=lowbit(x)) c[x]+=v;
}

void modify(int l,int r,int v) {
	add(l,v); add(r+1,-v);
}

bool cmp1(line a,line b) {
	return a.x<b.x;
}

bool cmp2(Q a,Q b) {
	return a.x<b.x;
}

bool cmp3(squr a,squr b) {
	return a.w<b.w;
}

void solve(int l,int r,int tl,int tr) {
	if (tl>tr) return ;
	if (l==r) {
		rep(i,tl,tr) ans[q[i].id]=l;
		return ;
	}
	int mid=(l+r)>>1,rec;
	std:: vector <line> u;
	std:: vector <Q> v,L,R;
	rep(i,l,mid) {
		if (t[i].x3<=t[i].y3) {
			u.push_back((line) {t[i].x1,t[i].x2,t[i].y2,1});
			u.push_back((line) {t[i].y1+1,t[i].x2,t[i].y2,-1});

			u.push_back((line) {t[i].x2,t[i].x3,t[i].y3,1});
			u.push_back((line) {t[i].y2+1,t[i].x3,t[i].y3,-1});
		} else {
			u.push_back((line) {t[i].x1,t[i].x2,t[i].y2,1});
			u.push_back((line) {t[i].y1+1,t[i].x2,t[i].y2,-1});
		}
	}
	rep(i,tl,tr) v.push_back((Q) {q[i].x,q[i].y,q[i].k,i});
	std:: sort(u.begin(), u.end(),cmp1);
	std:: sort(v.begin(), v.end(),cmp2);
	rep(i,0,pos[0]) c[i]=0;
	for (int i=0,j=0;i<v.size();++i) {
		for (;j<u.size()&&u[j].x<=v[i].x;++j) {
			modify(u[j].l,u[j].r,u[j].v);
		}
		int res=get(v[i].y);
		if (res>=v[i].k) {
			L.push_back(q[v[i].id]);
			ans[q[v[i].id].id]=mid;
		} else {
			q[v[i].id].k-=res;
			R.push_back(q[v[i].id]);
		}
	}
	rec=L.size();
	for (int i=0;i<rec;++i) q[tl+i]=L[i];
	for (int i=0;i<R.size();++i) q[tl+rec+i]=R[i];
	solve(l,mid,tl,tl+rec-1);
	solve(mid+1,r,tl+rec,tr);
}

int get_lca(int x,int y) {
	for (;bl[x]!=bl[y];x=fa[bl[x]]) {
		if (dep[bl[x]]<dep[bl[y]]) std:: swap(x,y);
	}
	return dep[x]<dep[y]?x:y;
}

int get_up(int x,int y) {
	for (;bl[x]!=bl[y];x=fa[bl[x]]) {
		if (dep[bl[x]]<dep[bl[y]]) std:: swap(x,y);
		if (fa[x]==y) return x;
		if (fa[bl[x]]==y) return bl[x];
	}
	for (int i=ls[y];i;i=e[i].next) {
		if (e[i].y!=fa[y]&&bl[e[i].y]==bl[y]) return e[i].y;
	}
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	int n=read(),m=read(),C=read();
	rep(i,2,n) add_edge(read(),read());
	dfs1(dep[1]=1); dfs2(1,1);
	rep(i,1,m) {
		int x=read(),y=read(),w=read();
		if (pos[x]>pos[y]) std:: swap(x,y);
		if (get_lca(y,x)==x) {
			int z=get_up(y,x);
			t[i]=(squr) {1,pos[z]-1,pos[y],pos[y]+size[y]-1,pos[z]+size[z],pos[0],w};
		} else t[i]=(squr) {pos[x],pos[x]+size[x]-1,pos[y],pos[y]+size[y]-1,0,-1,w};
	}
	std:: sort(t+1,t+m+1,cmp3);
	rep(i,1,C) {
		int x=read(),y=read();
		if (pos[x]>pos[y]) std:: swap(x,y);
		q[i].x=pos[x],q[i].y=pos[y];
		q[i].id=i,q[i].k=read();
	}
	solve(1,m,1,C);
	rep(i,1,C) printf("%d\n", t[ans[i]].w);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值