bzoj 4009: [HNOI2015]接水果

Description

风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果。由于她已经DT FC 了The big black,  她觉得这个游戏太简单了,于是发明了一个更加难的版本。首先有一个地图,是一棵由 n 个顶点、n-1 条边组成的树(例如图 1给出的树包含 8 个顶点、7 条边)。这颗树上有 P 个盘子,每个盘子实际上是一条
路径(例如图 1 中顶点 6 到顶点 8 的路径),并且每个盘子还有一个权值。第 i 盘子就是顶点a_i到顶点b_i的路径(由于是树,所以从a_i到b_i的路径是唯一的),权值为c_i。接下来依次会有Q个水果掉下来,每个水果本质上也是一条路径,第i 个水果是从顶点 u_i 到顶点v_i 的路径。幽香每次需要选择一个盘子去接当前的水
果:一个盘子能接住一个水果,当且仅当盘子的路径是水果的路径的子路径(例如图1中从 3到7 的路径是从1到8的路径的子路径)。这里规定:从a 到b的路径与从b到 a的路径是同一条路径。当然为了提高难度,对于第 i 个水果,你需要选择能接住它的所有盘子中,权值第 k_i 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗? 

        这题的转化真是妙啊。

        对于一条路径x-y,(设dfn[x]<dfn[y]),当x不是 x和y的 lca时, 如果 x-y 是 x1-y1的子路径 ,那么 要求 

                                                     in[x]<=dfn[x1]<=out[x]    &&  in[y]<=dfn[y1]<=out[y]

        如果x是x和y的lca时,设w为x->y的路径上x的第一个儿子, 那么在w上面的点以及不在x->某个点的子树的点都是有可能的,画一下发现如果 x-y 是 x1-y1的子路径,那么要求

                (1<=dfn[x1]<=in[w] &&   in[y]<=dfn[y1]<=out[y]) ||  (in[y]<=dfn[x1]<=out[y] && in[w]+1<=dfn[y]<=n)

          发现一个盘子可以被拆分成一个矩形或者是两个互不相交的矩形,那么问题就变成了,给出一堆点,求覆盖它的矩形权值第k小的是谁,这样就可以用整体二分加扫描线解决了,具体如下。

          先对点的x进行排序,对于矩形按照权值排序,

          整体二分的过程中,对于矩形,将上下边界提取出来,排序,枚举当前区间的每一个点,将矩形的边界更新到当前的x,如果一个点被覆盖的次数>=k了,说明答案肯定肯定更小,放到左边递归,如果不满足,就把它的k减去当前的覆盖次数,放到右边递归即可。

          下附AC代码。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define maxn 200005
#define mid ((nl+nr)>>1)
using namespace std;
struct nod1
{
	int x1,y1,x2,y2,val;
	nod1(int a,int b,int c,int d,int e)
	{
		x1=a;
		y1=b;
		x2=c;
		y2=d;
		val=e;
	}
	nod1(){}
}a[maxn];
struct nod2
{
	int x,y,k,id;
	nod2(int a,int b,int c,int d)
	{
		x=a;
		y=b;
		k=c;
		id=d;
	}
	nod2(){}
}b[maxn],temp1[maxn],temp3[maxn];
struct nod3
{
	int l,r,x,f;
	nod3(int a,int b,int c,int d)
	{
		l=a;
		r=b;
		x=c;
		f=d;
	}
	nod3(){}
}temp2[maxn];
bool operator<(nod1 a,nod1 b)
{
	return a.val<b.val;
}
bool operator<(nod2 a,nod2 b)
{
	return a.x<b.x;
}
bool operator<(nod3 a,nod3 b)
{
	return a.x<b.x;
}
int n,m,q,tot,sz;
int head[maxn],nex[maxn<<1],to[maxn<<1];
void add1(int x,int y)
{
	to[++tot]=y;nex[tot]=head[x];head[x]=tot;
}
int anc[maxn][20],dep[maxn],dfn[maxn],out[maxn],ans[maxn];
void dfs(int now,int fa)
{
	dfn[now]=++sz;
	for(int i=1;(1<<i)<=dep[now];i++)
		anc[now][i]=(anc[anc[now][i-1]][i-1]);
	for(int i=head[now];i;i=nex[i])
	{
		if(to[i]!=fa)
		{
			anc[to[i]][0]=now;
			dep[to[i]]=dep[now]+1;
			dfs(to[i],now);
		}
	}
	out[now]=sz;
}
int lca(int p,int q)
{
	if(dep[p]<dep[q]) swap(p,q);
	int d=(dep[p]-dep[q]);
	for(int i=0;i<=18;i++)
		if((1<<i)&d)
			p=anc[p][i];
	if(p==q) return p;
	for(int i=18;i>=0;i--)
		if(anc[p][i]!=anc[q][i])
			p=anc[p][i],q=anc[q][i];
	return anc[p][0];
}
int dat[maxn];
int lowbit(int now)
{
	return (now&(-now));
}
void add(int pos,int val)
{
	for(int i=pos;i<=n;i+=lowbit(i))
		dat[i]+=val;
}
int query(int pos)
{
	int res=0;
	for(int i=pos;i>=1;i-=lowbit(i))
		res+=dat[i];
	return res;
}
int lca2(int p,int q)
{
	for(int i=18;i>=0;i--)
		if(dep[anc[p][i]]>dep[q])
			p=anc[p][i];
	return p;
}
void solve(int nl,int nr,int ql,int qr)
{
//	cerr<<"Its "<<nl<<" "<<nr<<" "<<ql<<" "<<qr<<endl;
	if(ql>qr) return;
	if(nl==nr)
	{
		for(int i=ql;i<=qr;i++)
			ans[b[i].id]=a[nl].val;
		return;
	}
	int top=0;
	for(int i=nl;i<=mid;i++)
	{
		temp2[++top]=nod3(a[i].y1,a[i].y2,a[i].x1,1);
		temp2[++top]=nod3(a[i].y1,a[i].y2,a[i].x2+1,-1);
	}
	sort(temp2+1,temp2+1+top);
//	for(int i=1;i<=top;i++)
//		cerr<<temp2[i].l<<" "<<temp2[i].r<<" "<<temp2[i].x<<" "<<temp2[i].f<<endl;
	int s=1,top1=0,top2=0;
	for(int i=ql;i<=qr;i++)
	{
		while(s<=top && temp2[s].x<=b[i].x)
			add(temp2[s].l,temp2[s].f),add(temp2[s].r+1,-temp2[s].f),s++;
		int temp=query(b[i].y);
		if(temp>=b[i].k)
			temp1[++top1]=b[i];
		else
			temp3[++top2]=b[i],temp3[top2].k-=temp;
	}
	while(s<=top)
		add(temp2[s].l,temp2[s].f),add(temp2[s].r+1,-temp2[s].f),s++;
	s=ql;
	for(int i=1;i<=top1;i++)
		b[s++]=temp1[i];
	int t=s-1;
	for(int i=1;i<=top2;i++)
		b[s++]=temp3[i];
	solve(nl,mid,ql,t);
	solve(mid+1,nr,t+1,qr);
}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add1(x,y); add1(y,x);
	}
	dfs(1,-1);
	tot=0;
	for(int i=1;i<=m;i++)
	{
		int x,y,v;
		scanf("%d%d%d",&x,&y,&v);
		if(dfn[x]>dfn[y]) swap(x,y);
		int l=lca(x,y);
//		cerr<<"its "<<l<<endl;
		if(l!=x)
		{
			a[++tot]=nod1(dfn[x],dfn[y],out[x],out[y],v);
		}
		else
		{
			int w=lca2(y,x);
//			cerr<<"its "<<w<<endl;
			a[++tot]=nod1(1,dfn[y],dfn[w]-1,out[y],v);
			if(out[w]+1<=n)
			{
				a[++tot]=nod1(dfn[y],out[w]+1,out[y],n,v);
			}
		}
	}
//	cerr<<"+1"<<endl;
	for(int i=1;i<=q;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z); if(dfn[x]>dfn[y]) swap(x,y);
		b[i]=nod2(dfn[x],dfn[y],z,i);
	}
//	cerr<<"+2"<<endl;
	sort(a+1,a+1+tot);
	sort(b+1,b+1+q);
//	for(int i=1;i<=tot;i++)
//	{
//		cerr<<a[i].x1<<" "<<a[i].y1<<" "<<a[i].x2<<" "<<a[i].y2<<" "<<a[i].val<<endl;
//	}
	solve(1,tot,1,q);
	for(int i=1;i<=q;i++)
		printf("%d\n",ans[i]);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值