BZOJ 4009: [HNOI2015]接水果 【整体二分+矩形覆盖(dfs序解决树上路径包含问题)】

BZOJ传送门
洛谷传送门

题目分析:

权值第k小很好处理,整体二分就可以了。
再一看路径包含,什么玩意??
然后就被dalao的分析震惊了
居然可以转化成dfs序,判断x,y路径在盘子子树内、子树外。

若x与y不成祖先-孩子关系,则包含它的路径u-v满足:起点在x的子树里,且终点在y的子树里。
若x与y成祖先-孩子关系,假设y是x的祖先,z是y到x方向的第一个节点,则包含它的路径u-v满足:起点在x的子树里,且终点不在z的子树里。

我就只补充一下怎么求一个点被矩形覆盖的次数。
这是个二维偏序问题,一维排序,一维树状数组就可以解决了
具体做法就是把,把一个矩形变成两条在x1,x2处的两条(y1,y2)线段,然后把矩形和询问都按照x轴排序,矩形的y轴线段在树状数组中差分。查询就直接按y查树状数组。

Code:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<vector>
#include<algorithm>
#define maxn 40005
#define LL long long
using namespace std;
inline void read(int &a){
    char c;bool f=0;
	while(!isdigit(c=getchar())) if(c=='-') f=1;
    for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
	if(f) a=-a;
}
int n,m,Q,cnt,ans[maxn],sum[maxn];
int in[maxn],out[maxn],tim,f[maxn][20],dep[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
struct matrix{
	int x1,x2,y1,y2,val;
	bool operator < (const matrix &p)const{return val<p.val;}
}a[maxn<<1],b[maxn<<2];
bool cmpx(const matrix &a,const matrix &b){return a.x1<b.x1;}
struct node{
	int x,y,id,k;
	bool operator < (const node &p)const{return x<p.x;}
}q[maxn],qt[maxn];
inline void line(int x,int y){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;}
void dfs(int u){
	in[u]=++tim;
	for(int i=fir[u];i;i=nxt[i])
		if(!in[to[i]]) f[to[i]][0]=u,dep[to[i]]=dep[u]+1,dfs(to[i]);
	out[u]=tim;
}
int find(int u,int v){
	int k=dep[u]-dep[v]-1;
	for(int i=17;i>=0;i--) if(k&(1<<i)) u=f[u][i];
	return u;
}
int arr[maxn];
void update(int i,int d){for(;i<=n;i+=i&-i) arr[i]+=d;}
int getsum(int i){int s=0;for(;i;i-=i&-i) s+=arr[i];return s;}
void calc(int h,int t,int vl,int vr){
	for(int i=h;i<=t;i++) sum[i]=0;
	int l=lower_bound(a+1,a+1+cnt,(matrix){0,0,0,0,vl})-a,num=0;
	if(l>cnt) return;
	for(int i=l;i<=cnt&&a[i].val<=vr;i++){
		b[++num]=(matrix){a[i].x1,0,a[i].y1,a[i].y2,1};
		b[++num]=(matrix){a[i].x2+1,0,a[i].y1,a[i].y2,-1};
	}
	sort(b+1,b+1+num,cmpx);
	for(int i=1,j=h;i<=num||j<=t;){
		int x=i>num?q[j].x:j>t?b[i].x1:min(b[i].x1,q[j].x);
		while(i<=num&&b[i].x1<=x) update(b[i].y1,b[i].val),update(b[i].y2+1,-b[i].val),i++;
		while(j<=t&&q[j].x<=x) sum[j]=getsum(q[j].y),j++;
	}
}
void solve(int h,int t,int vl,int vr){
	if(h>t) return;
	if(vl==vr) {for(int i=h;i<=t;i++) ans[q[i].id]=vl;return;}
	int vmid=(vl+vr)>>1;
	calc(h,t,vl,vmid);
	int st=h-1;
	for(int i=h;i<=t;i++) if(q[i].k<=sum[i]) qt[++st]=q[i];
	int mid=st;
	for(int i=h;i<=t;i++) if(q[i].k>sum[i]) q[i].k-=sum[i],qt[++st]=q[i];
	for(int i=h;i<=t;i++) q[i]=qt[i];
	solve(h,mid,vl,vmid),solve(mid+1,t,vmid+1,vr);
}
int main()
{
	int x,y,z,w;
	read(n),read(m),read(Q);
	for(int i=1;i<n;i++) read(x),read(y),line(x,y),line(y,x);
	dfs(1);
	for(int j=1;j<=17;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
	for(int i=1;i<=m;i++){
		read(x),read(y),read(w);
		if(in[x]>in[y]) swap(x,y);
		if(in[x]<=in[y]&&out[y]<=out[x]){
			z=find(y,x);
			if(in[z]>1) a[++cnt]=(matrix){1,in[z]-1,in[y],out[y],w};
			if(out[z]<n) a[++cnt]=(matrix){in[y],out[y],out[z]+1,n,w};
		}
		else a[++cnt]=(matrix){in[x],out[x],in[y],out[y],w};
	}
	for(int i=1;i<=Q;i++){
		read(x),read(y),read(q[i].k),q[i].id=i;
		q[i].x=min(in[x],in[y]),q[i].y=max(in[x],in[y]);
	}
	sort(a+1,a+1+cnt);
	sort(q+1,q+1+Q);
	solve(1,Q,0,1e9);
	for(int i=1;i<=Q;i++) printf("%d\n",ans[i]);
}

一开始打的时候把矩形直接放到了vector里面每次O(n)循环,没有用排序,然而复杂度是不对的,整体二分是一层的总和O(n),而不是一段O(n),TLE到爆炸。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值