BZOJ 4009: [HNOI2015]接水果(整体二分套二维偏序)

130 篇文章 1 订阅
86 篇文章 0 订阅

题目
表示这种题好套路啊。
原题如果注意到一条路径包含另一条路径,那么在dfs序上第一条路径的端点与第二条路径的端点是有一个偏序关系的。
然后就变成了二分答案+三维偏序。
因为可以离线,
将二分答案和一维整合起来变成整体二分 O ( n log ⁡ n ) O(n\log n) O(nlogn)
剩下两维就是一个二维偏序,排序后树状数组维护 O ( log ⁡ n ) O(\log n) O(logn)
一共 O ( n log ⁡ 2 n ) O(n\log ^2n) O(nlog2n)
AC Code:

#include<bits/stdc++.h>
#define maxn 40005
using namespace std;

int n,P,Q,m;
int sb[maxn];
int info[maxn],Prev[maxn<<1],to[maxn<<1],cnt_e=0;
void Node(int u,int v){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v;}

int st[maxn],ed[maxn],tot,fa[maxn];
void dfs(int now,int ff){
	st[now] = ++tot;
	fa[now] = ff;
	for(int i=info[now];i;i=Prev[i])
		if(to[i]!=ff)
			dfs(to[i],now);
	ed[now] = tot;
}

struct Plate{
	int a,b,c;
	bool operator <(const Plate &B)const{ return c==B.c?a==B.a?b<B.b:a<B.a:c<B.c; }
}p[maxn];
struct Query{
	int u,v,k;
	bool operator <(const Query &B)const{ return k==B.k?u==B.u?v<B.v:u<B.u:k<B.k; }
}q[maxn];
struct Modify{
	int u,l,r,v,val;
	Modify(int u=0,int l=0,int r=0,int v=0,int val=0):u(u),l(l),r(r),v(v),val(val){}
}md[maxn*10];
bool cmp1(const int &u,const int &v){ return md[u].val==md[v].val?md[u].v<md[v].v:md[u].val<md[v].val; }
bool cmp2(const int &u,const int &v){ return md[u].u<md[v].u; }
bool cmp3(const int &u,const int &v){ return q[u].u < q[v].u; }
int cm[maxn*10],cq[maxn],q1[maxn],q2[maxn],ans[maxn];
int tr[maxn];
inline void upd(int now,int val){ for(;now<=n;now+=now&-now) tr[now]+=val; }
inline int qsum(int now,int ret=0){for(;now;now-=now&-now) ret+=tr[now];return ret;  }

void Solve(int l,int r,int ml,int mr,int ql,int qr){
	if(ml > mr || ql > qr) return;
	if(l==r){
		for(int i=ql;i<=qr;i++)
			ans[cq[i]] = sb[l];
		return;
	}
	int mid = (l+r) >> 1 , cnt = ml;
	for(;md[cm[cnt]].val<=mid;cnt++);
	sort(cm+ml,cm+cnt,cmp2);
	int j = ml , r1 = 0 , r2 = 0;
	for(int i=ql;i<=qr;i++){
		//printf("%d %d\n",i,q[cq[i]].u);
		for(;j<cnt && md[cm[j]].u<=q[cq[i]].u;j++){
			upd(md[cm[j]].l,md[cm[j]].v) , upd(md[cm[j]].r+1,-md[cm[j]].v);
		}
		int tmp;
		if((tmp = qsum(q[cq[i]].v)) < q[cq[i]].k)
			q2[r2++] = cq[i] , q[cq[i]].k-=tmp;
		else q1[r1++] = cq[i];
	}
	//puts("");
	for(int i=ql;i<ql+r1;i++) cq[i]=q1[i-ql];
	for(int i=ql+r1;i<=qr;i++) cq[i]=q2[i-ql-r1];
	for(j--;j>=ml;j--) upd(md[cm[j]].l,-md[cm[j]].v) , upd(md[cm[j]].r+1,md[cm[j]].v);
	sort(cm+ml,cm+mr+1,cmp1);
	Solve(l,mid,ml,cnt-1,ql,ql+r1-1),Solve(mid+1,r,cnt,mr,ql+r1,qr);
}

int main(){
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	scanf("%d%d%d",&n,&P,&Q);
	for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),Node(u,v),Node(v,u);
	dfs(1,0);
	for(int i=1;i<=P;i++) scanf("%d%d%d",&p[i].a,&p[i].b,&p[i].c),sb[++sb[0]]=p[i].c;
	for(int i=1;i<=Q;i++){
		scanf("%d%d%d",&q[i].u,&q[i].v,&q[i].k),cq[i]=i;
		if(st[q[i].u] > st[q[i].v]) swap(q[i].u,q[i].v);
		q[i].u = st[q[i].u] , q[i].v = st[q[i].v];
	}
	sort(sb+1,sb+1+sb[0]);
	sb[0]=unique(sb+1,sb+1+sb[0])-sb-1;
	for(int i=1;i<=P;i++) p[i].c = lower_bound(sb+1,sb+1+sb[0],p[i].c)-sb;
		
	for(int i=1;i<=P;i++){
		if(st[p[i].a] > st[p[i].b]) swap(p[i].a,p[i].b);
		if(ed[p[i].a] < st[p[i].b]){
			md[++m] = Modify(st[p[i].a],st[p[i].b],ed[p[i].b],1,p[i].c),cm[m]=m;
			md[++m] = Modify(ed[p[i].a]+1,st[p[i].b],ed[p[i].b],-1,p[i].c),cm[m]=m;
		}
		else{
			int j;
			for(j=info[p[i].a];j;j=Prev[j])
				if(st[to[j]]<=st[p[i].b] && ed[p[i].b]<=ed[to[j]] && to[j]!=fa[p[i].a])
					break;
			j = to[j];
			md[++m] = Modify(1,st[p[i].b],ed[p[i].b],1,p[i].c),cm[m]=m;
			md[++m] = Modify(st[j],st[p[i].b],ed[p[i].b],-1,p[i].c),cm[m]=m;
			md[++m] = Modify(st[p[i].b],ed[j]+1,n,1,p[i].c),cm[m]=m;
			md[++m] = Modify(ed[p[i].b]+1,ed[j]+1,n,-1,p[i].c),cm[m]=m;
		}
	}		
	sort(cm+1,cm+1+m,cmp1);
	sort(cq+1,cq+1+Q,cmp3);
	Solve(1,sb[0],1,m,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、付费专栏及课程。

余额充值