【BZOJ4009】[HNOI2015]接水果 DFS序+整体二分+扫描线+树状数组
数组 二分 DFS 整体 树状 水果 扫描线
2023-09-11 14:15:25 时间
【BZOJ4009】[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 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗?
Input
第一行三个数 n和P 和Q,表示树的大小和盘子的个数和水果的个数。
接下来n-1 行,每行两个数 a、b,表示树上的a和b 之间有一条边。树中顶点
按1到 n标号。 接下来 P 行,每行三个数 a、b、c,表示路径为 a 到 b、权值为 c 的盘子,其中0≤c≤10^9,a不等于b。
接下来Q行,每行三个数 u、v、k,表示路径为 u到 v的水果,其中 u不等于v,你需要选择第 k小的盘子,第k 小一定存在。
Output
对于每个果子,输出一行表示选择的盘子的权值。
Sample Input
10 10 10
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
3 2 217394434
10 7 13022269
6 7 283254485
6 8 333042360
4 6 442139372
8 3 225045590
10 4 922205209
10 8 808296330
9 2 486331361
4 9 551176338
1 8 5
3 8 3
3 8 4
1 8 3
4 8 1
2 3 1
2 3 1
2 3 1
2 4 1
1 4 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
3 2 217394434
10 7 13022269
6 7 283254485
6 8 333042360
4 6 442139372
8 3 225045590
10 4 922205209
10 8 808296330
9 2 486331361
4 9 551176338
1 8 5
3 8 3
3 8 4
1 8 3
4 8 1
2 3 1
2 3 1
2 3 1
2 4 1
1 4 1
Sample Output
442139372
333042360
442139372
283254485
283254485
217394434
217394434
217394434
217394434
217394434
333042360
442139372
283254485
283254485
217394434
217394434
217394434
217394434
217394434
HINT
N,P,Q<=40000。
题解:觉得很熟悉?赶紧去复习精神污染吧!
设入栈序p,出栈序q,那么a-b是x-y的子路径有以下两种情况(p[a]<p[b],p[x]<p[y])。
a是b的祖先,那么设b在a的c儿子的子树中,则有1<=p[x]<p[c],q[c]<p[y]<=n。
a不是b的祖先,则有p[a]<=p[x]<=q[a],p[b]<=p[y]<=q[b]。
这就变成了平面上有一堆矩形,每个矩形都有一个权值,每次询问一个点,问在所有包含这个点的矩形中,权值第k小的权值是多少。可以用树套树做,当然,用整体二分+扫描线+树状数组区间修改也是极好的啦~
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int maxn=40010; int n,tot,tp,m1,m2,cnt,now; int to[maxn<<1],next[maxn<<1],head[maxn],p1[maxn],p2[maxn],fa[16][maxn],dep[maxn],ans[maxn],s[maxn],vis[maxn]; struct operation { int a1,b1,a2,b2,v; operation() {} operation(int _1,int _2,int _3,int _4,int _5) {a1=_1,b1=_2,a2=_3,b2=_4,v=_5;} }p[maxn<<1]; struct ask { int a,b,v,sl,org; ask() {} ask(int _1,int _2,int _3,int _4) {a=_1,b=_2,v=_3,org=_4;} }q[maxn],qq[maxn]; struct node { int x,a,b,v; node() {} node(int _1,int _2,int _3,int _4) {x=_1,a=_2,b=_3,v=_4;} }pp[maxn<<1]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } void add(int a,int b) { to[cnt]=b,next[cnt]=head[a],head[a]=cnt++; } void dfs(int x) { p1[x]=++p1[0]; for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[0][x]) fa[0][to[i]]=x,dep[to[i]]=dep[x]+1,dfs(to[i]); p2[x]=p1[0]; } void updata(int x,int v) { for(int i=x;i<=n;i+=i&-i) { if(vis[i]<now) vis[i]=now,s[i]=0; s[i]+=v; } } int query(int x) { int i,ret=0; for(i=x;i;i-=i&-i) { if(vis[i]<now) vis[i]=now,s[i]=0; ret+=s[i]; } return ret; } bool cmpx(node a,node b) { return a.x<b.x; } bool cmpa(ask a,ask b) { return a.a<b.a; } bool cmpv(operation a,operation b) { return a.v<b.v; } void solve(int l,int r,int L,int R) { if(l==r||L>R) { for(int i=L;i<=R;i++) ans[q[i].org]=p[l].v; return ; } sort(p+l,p+r+1,cmpv); int mid=(l+r)>>1,MID=L-1,i,j; for(tp=0,now++,i=l;i<=mid;i++) pp[++tp]=node(p[i].a1,p[i].a2,p[i].b2,1),pp[++tp]=node(p[i].b1+1,p[i].a2,p[i].b2,-1); sort(pp+1,pp+tp+1,cmpx),sort(q+L,q+R+1,cmpa); for(i=L,j=1;i<=R;i++) { for(;j<=tp&&pp[j].x<=q[i].a;j++) updata(pp[j].a,pp[j].v),updata(pp[j].b+1,-pp[j].v); q[i].sl=query(q[i].b),MID+=(q[i].sl>=q[i].v); } int h1=L,h2=MID+1; for(i=L;i<=R;i++) { if(q[i].sl>=q[i].v) qq[h1++]=q[i]; else q[i].v-=q[i].sl,qq[h2++]=q[i]; } for(i=L;i<=R;i++) q[i]=qq[i]; solve(l,mid,L,MID),solve(mid+1,r,MID+1,R); } int FA(int x,int y) { for(int j=15;j>=0;j--) if((1<<j)<=y) y-=(1<<j),x=fa[j][x]; return x; } int main() { //freopen("bz4009.in","r",stdin); n=rd(),m1=rd(),m2=rd(); int i,j,a,b,c,d; memset(head,-1,sizeof(head)); for(i=1;i<n;i++) a=rd(),b=rd(),add(a,b),add(b,a); dep[1]=1,dfs(1); for(j=1;(1<<j)<=n;j++) for(i=1;i<=n;i++) fa[j][i]=fa[j-1][fa[j-1][i]]; for(i=1;i<=m1;i++) { a=rd(),b=rd(),c=rd(); if(p1[a]>p1[b]) swap(a,b); if(p2[a]>=p2[b]) { d=FA(b,dep[b]-dep[a]-1); p[++tot]=operation(1,p1[d]-1,p1[b],p2[b],c); if(p2[b]<n) p[++tot]=operation(p1[b],p2[b],p2[d]+1,n,c); } else p[++tot]=operation(p1[a],p2[a],p1[b],p2[b],c); } for(i=1;i<=m2;i++) { a=rd(),b=rd(); if(p1[a]>p1[b]) swap(a,b); q[i]=ask(p1[a],p1[b],rd(),i); } solve(1,tot,1,m2); for(i=1;i<=m2;i++) printf("%d\n",ans[i]); return 0; }//4 4 1 1 2 2 3 1 4 1 2 2 2 3 3 1 4 4 4 1 1 2 4 1
相关文章
- 33. 搜索旋转排序数组,二分查找某个数
- 【C/C++学院】(3)二维数组/二分查找法/指针/模块注射
- PHP精选数组函数
- Java实现 LeetCode 659 分割数组为连续子序列 (哈希)
- Java实现 LeetCode 581 最短无序连续子数组(从两遍搜索找两个指针)
- Java实现 LeetCode 421 数组中两个数的最大异或值
- 1.ES5 与 ES6 遍历数组的不同方法
- 51单片机串口接收一个数组
- (算法:二分查找)在排序数组中,找出给定数字出现的次数
- 【二分】LeetCode 33. 搜索旋转排序数组【中等】
- 152.乘积最大子数组
- Android JNI数组的处理
- 【LeetCode 简单 数组】35 搜索插入位置
- 2108. 找出数组中的第一个回文字符串
- 152. 乘积最大子数组-动态规划
- Leetcode 453. 最小操作次数使数组元素相等(未解决,仅供参考)
- VB编程:对数组进行二分查找-29_彭世瑜_新浪博客
- [LeetCode] 33. 搜索旋转排序数组 ☆☆☆(二分查找)
- POJ 1195 Mobile phones (二维树状数组)
- Swift数组
- 旋转数组的二分查找 代码重构 追求优雅的代码
- 博弈型动态规划模板——精髓:把两个选手当成一个人,每次面对a[i…j]选最优解,用dfs+cache做最直观,再考虑修改为dp数组
- 二分法应用——搜索旋转数组,以前一直在纠结a[0],a[-1],a[mid], target三者关系,其实最简单的还是使用2次二分,先找到旋转数组peak,然后用正常的二分搜索即可!
- 【Leetcode刷题Python】34. 在排序数组中查找元素的第一个和最后一个位置(二分查找)