[HNOI2015]接水果 (洛谷3242/bzoj4009)

洛谷应该是疯了,又给我推荐黑题,又花了一上午

闲话少说,先给链接

大致题意描述(语文不好请见谅):

给一棵树,再给P条路径,每条路径有权值。

有Q次询问,每次给一条路径和k,让你输出能完全覆盖这条路径的 第k大路径(的权值)

(a --> b) & (b --> a)  are the same


我知道您肯定没看懂,所以您还是去打开链接看吧


先考虑如何得到完全覆盖的路径:

设水果路径起点为x,终点为y,且dep[x]

设盘子路径起点为a,终点为b

设一点dfs序为dfn,子树dfn最大的点的dfn为low(即dfn~low为这一棵子树的所有节点)

若lca(x,y)==x:

        设w为 x --> y 路径上的第一个点(不包括x)

        则有a在y子树内,b不在w子树内

        即:dfn[y]<=dfn[a]<=low[y],且(1<=dfn[b]

else:

        a、b两点都分别在x、y子树内

        即:dfn[x]<=dfn[a]<=low[x],且dfn[y]<=dfn[b]<=low[y]

可以看出,我们成功把盘子变成了1或2个矩形,水果变成了点

那么题目转换为:求覆盖一个点的权值第k大的矩形(的权值)

考虑整体二分(不会的左转百度,因为我也不会

在二分过程中,先用扫描线算法把矩形优化为一维线

然后将区间加改为差分,用树状数组维护前缀和(即单点值)

就可以求出每个点被覆盖了多少次辣

代码:

#include
#define sz 40040
using namespace std;
int n,m,Q,i,x,y,z,q[sz],tmp[sz],now[sz],ans[sz];
struct E{
	int x1,x2,l1,r1,l2,r2,z;
	E(){}
	E(int _x1,int _x2,int _l1,int _r1,int _l2,int _r2,int _z){x1=_x1,x2=_x2,l1=_l1,r1=_r1,l2=_l2,r2=_r2,z=_z;}
}e[sz];//盘子
inline bool cmp(const E&a,const E&b){return a.zsize[son[x]]) son[x]=o;
	}
}
void dfs2(int x,int tp)
{
	dfn[x]=++cnt;top[x]=tp;
	if (!son[x]) return (void)(low[x]=cnt);
	dfs2(son[x],tp);
	go(x) if (!dfn[o]) dfs2(o,o);
	low[x]=cnt;
}
int lca_son(int x,int y)
{
	int t;
	if (dep[x]'9'||ch<'0') ch=getchar();
	while (ch<='9'&&ch>='0') ret=ret*10+ch-48,ch=getchar();
	return ret;
}
void init()
{
	n=read();m=read();Q=read();
	int i,j,x,y,z;
	for (i=1;iqr) return;
	int i,j,L=ql,R=qr;
	if (l==r) {for (i=ql;i<=qr;i++) ans[a[q[i]].p]=e[l].z,cout<<(a[q[i]].k>1?"jkhh":"");return;}
	int mid=(l+r)>>1,cb=0,cc=0;
	for (i=l;i<=mid;i++)
	{
		if (e[i].l1<=e[i].r1)
		{
			b[++cb]=B(e[i].x1,e[i].l1,e[i].r1,1);
			b[++cb]=B(e[i].x2+1,e[i].l1,e[i].r1,-1);
		}
		if (e[i].l2<=e[i].r2)
		{
			b[++cb]=B(e[i].x1,e[i].l2,e[i].r2,1);
			b[++cb]=B(e[i].x2+1,e[i].l2,e[i].r2,-1);
		}
	}
	for (i=ql;i<=qr;i++)//一个拆成俩(别问我为什么,我也不知道)
	{
		c[++cc]=C(a[q[i]].x,a[q[i]].y,i);
		c[++cc]=C(a[q[i]].y,a[q[i]].x,i);
		now[i]=0;
	}
	T++,sort(b+1,b+cb+1,cmpb),sort(c+1,c+cc+1,cmpc);
	for (i=1,j=1;i<=cc;i++)
	{
		while (j<=cb&&b[j].x<=c[i].x) add(b[j].l,b[j].t),add(b[j].r+1,-b[j].t),j++;//差分加扫描线
		now[c[i].p]+=query(c[i].y);
	}
	for (i=ql;i<=qr;i++) if (now[i]>=a[q[i]].k) tmp[L++]=q[i];else {tmp[R--]=q[i];a[q[i]].k-=now[i];}
	for (i=ql;i<=qr;i++) q[i]=tmp[i];
	solve(l,mid,ql,R);solve(mid+1,r,R+1,qr);
}
int main()
{
	init();
	solve(1,m,1,Q);
	for (int i=1;i<=Q;i++) printf("%d\n",ans[i]);
}

你可能感兴趣的:(题解,数据结构)