bzoj4009: [HNOI2015]接水果

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4009

思路:先给定一些路径,每个路径有一个权值。

题目要求的是对于一个路径,它的子路径中权值第k大的是多少


首先我们观察一个盘子(u,v),它能接到哪些水果呢?

分情况:如果u!=lca(u,v)

bzoj4009: [HNOI2015]接水果_第1张图片

那么水果的两端点(a,b)就在盘子两端点的子树中

用dfs序来表示,就是dfn[u]<=a<=last[u],dfn[v]<=b<=last[v]

last[i]表示i的子树的最大 dfn

如果u==lca(u,v)

bzoj4009: [HNOI2015]接水果_第2张图片

这时稍微有一些区别,w表示u的儿子且是v的祖先的点,注意不是u

那么b还是在v子树中,a在除了w子树之外的所有点中

dfn[v]<=b<=last[v],1<=a<=dfn[w]-1||last[w]+1<=a<=n


这时,盘子就成了一个或两个矩形,水果就是点

问题就是求覆盖一个点的矩形中权值第k大的权值是多少》

扫描线+整体二分就可以了

我们先将矩形按权值从小到大排序

然后对于一个点,如果[l,mid]中能覆盖这个点的矩形数不小于k,则说明答案在[l,mid]中

否则在[mid+1,r],同时k减去覆盖的矩形数


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=80010,maxk=18;
using namespace std;
int n,m,q,cnt,fa[maxn][maxk],dfn[maxn],tim,last[maxn],dep[maxn],ans[maxn],sum[maxn];
int pre[maxn],now[maxn],son[maxn],tot;
struct Plate{int xd,xu,yd,yu,v;}plate[maxn];
struct Event{int x,yd,yu,v,id;}event[maxn];
struct Poi{int x,y,k,id;}poi[maxn],tmp1[maxn],tmp2[maxn];
bool operator <(Plate a,Plate b){return a.v<b.v;}
bool operator <(Event a,Event b){return a.x==b.x?a.id<b.id:a.x<b.x;}
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}

struct Bit{
	int val[maxn];
	void modify(int l,int r,int v){
		for (int i=l;i<=n;i+=(i&(-i))) val[i]+=v;
		for (int i=r+1;i<=n;i+=(i&(-i))) val[i]-=v;
	}
	int query(int x){int res=0;for (;x;x-=(x&(-x))) res+=val[x];return res;}
}T;

void dfs(int x){
	dfn[x]=++tim;
	for (int i=0;fa[x][i];i++) fa[x][i+1]=fa[fa[x][i]][i];
	for (int y=now[x];y;y=pre[y]) if (son[y]!=fa[x][0])
		fa[son[y]][0]=x,dep[son[y]]=dep[x]+1,dfs(son[y]);
	last[x]=tim;
}
int jump(int a,int h){for (int i=16;h;i--) if (h>=(1<<i)) h-=(1<<i),a=fa[a][i];return a;}
int lca(int a,int b){
	if (dep[a]<dep[b]) swap(a,b);
	a=jump(a,dep[a]-dep[b]);
	if (a==b) return a;
	for (int i=16;i>=0;i--) if (fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i];
	return fa[a][0];
}

void solve(int l,int r,int st,int ed){
	if (st>ed) return; 
	if (l==r){
		for (int i=st;i<=ed;i++) ans[poi[i].id]=plate[l].v;
		return;
	}
	int mid=(l+r)>>1,siz=0;
	for (int i=l;i<=mid;i++){
		event[++siz]=(Event){plate[i].xd,plate[i].yd,plate[i].yu,1,0};
		event[++siz]=(Event){plate[i].xu,plate[i].yd,plate[i].yu,-1,n+1};
	}
	for (int i=st;i<=ed;i++) event[++siz]=(Event){poi[i].x,poi[i].y,0,0,i};
	sort(event+1,event+1+siz);
	for (int i=1;i<=siz;i++) 
		if (st<=event[i].id&&event[i].id<=ed) sum[event[i].id]=T.query(event[i].yd);
		else T.modify(event[i].yd,event[i].yu,event[i].v);
	int a=0,b=0;
	for (int i=st;i<=ed;i++) 
		if (sum[i]>=poi[i].k) tmp1[++a]=poi[i];
		else tmp2[++b]=(Poi){poi[i].x,poi[i].y,poi[i].k-sum[i],poi[i].id};
	for (int i=st;i<=st+a-1;i++) poi[i]=tmp1[i-st+1];
	for (int i=st+a;i<=ed;i++) poi[i]=tmp2[i-st-a+1];
	solve(l,mid,st,st+a-1),solve(mid+1,r,st+a,ed);
}

int main(){
	scanf("%d%d%d",&n,&m,&q);
	for (int i=1,a,b;i<n;i++) scanf("%d%d",&a,&b),add(a,b),add(b,a);
	dfs(1);
	for (int i=1,a,b,c,u;i<=m;i++){
		scanf("%d%d%d",&a,&b,&c);u=lca(a,b);
		if (dfn[a]>dfn[b]) swap(a,b);
		if (u!=a) plate[++cnt]=(Plate){dfn[a],last[a],dfn[b],last[b],c};
		else{
			int w=jump(b,dep[b]-dep[a]-1);
			plate[++cnt]=(Plate){1,dfn[w]-1,dfn[b],last[b],c};
			if (last[w]<n) plate[++cnt]=(Plate){dfn[b],last[b],last[w]+1,n,c};
		}
	}
	sort(plate+1,plate+1+cnt);
	for (int i=1,a,b,k;i<=q;i++){
		scanf("%d%d%d",&a,&b,&k);
		if (dfn[a]>dfn[b]) swap(a,b);
		poi[i]=(Poi){dfn[a],dfn[b],k,i};
	}
	solve(1,cnt,1,q);
	for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
	return 0;
}




你可能感兴趣的:(bzoj4009: [HNOI2015]接水果)