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

这篇博客介绍了HNOI2015中的一道题目,涉及一棵树上的路径权值和覆盖问题。博主探讨了如何将问题转化为求解覆盖一个点的权值第k大的矩形,并通过二分法和树状数组来优化解决方案。主要内容包括如何找到完全覆盖路径,以及利用dfs序和低点进行路径分析。

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

闲话少说,先给链接

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

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

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

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


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


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

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

设盘子路径起点为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]<dfn[w] || low[w]<dfn[b]<=n)

else:

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

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

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

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

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

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

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

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

代码:

#include<bits/stdc++.h>
#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.z<b.z;}
struct P{int x,y,k,p;P(){}P(int _x,int _y,int _k,int _p){x=_x,y=_y,k=_k,p=_p;}}a[sz];//水果
struct B{int x,l,r,t;B(){}B(int _x,int _l,int _r,int _t){x=_x,l=_l,r=_r,t=_t;}}b[sz<<1];
inline bool cmpb(const B&a,const B&b){return a.x<b.x;}
struct C{int x,y,p;C(){}C(int _x,int _y,int _p){x=_x,y=_y,p=_p;}}c[sz<<1];
inline bool cmpc(const C&a,const C&b){return a.x<b.x;}
struct hh{int t,nxt;}edge[sz<<1];
int head[sz],ecnt;
void make_edge(int f,int t)
{
	edge[++ecnt]=(hh){t,head[f]};
	head[f]=ecnt;
	edge[++ecnt]=(hh){f,head[t]};
	head[t]=ecnt;
}
int T,pos[sz],bit[sz];
void add(int x,int y){while (x<=n){if (pos[x]<T) bit[x]=y;else bit[x]+=y;pos[x]=T;x+=(x&-x);}}
int query(int x){int ret=0;while (x){if (pos[x]==T) ret+=bit[x];x-=(x&-x);}return ret;}
int top[sz],son[sz],fa[sz],dep[sz],dfn[sz],low[sz],size[sz],cnt;
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define o edge[i].t
void dfs1(int x)
{
	size[x]=1;dep[x]=dep[fa[x]]+1;
	go(x) if (o!=fa[x])
	{
		fa[o]=x;
		dfs1(o);
		size[x]+=size[o];
		if (size[o]>size[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]<dep[y]) swap(x,y);
	while(top[x]!=top[y]) x=fa[t=top[x]];
	return x==y?t:son[y];
}
inline int read()
{
	register int ret=0;
	register char ch=getchar();
	while (ch>'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;i<n;i++) x=read(),y=read(),make_edge(x,y);
	dfs1(1);dfs2(1,1);
	for (i=1;i<=m;i++)
	{
		x=read();y=read();z=read();
		if (dep[x]<dep[y]) swap(x,y);
		if (dfn[y]<=dfn[x]&&low[x]<=low[y]) j=lca_son(x,y),e[i]=E(dfn[x],low[x],1,dfn[j]-1,low[j]+1,n,z);
		else e[i]=E(dfn[x],low[x],dfn[y],low[y],1,0,z);
	}
	sort(e+1,e+m+1,cmp);
	for (i=1;i<=Q;i++)
	{
		x=read(),y=read(),z=read();
		a[i]=P(dfn[x],dfn[y],z,i);q[i]=i;
	}
}
void solve(int l,int r,int ql,int qr)
{
	if (ql>qr) 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]);
}

根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.youkuaiyun.com/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.youkuaiyun.com/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值