JZOJ 4811. 排队(线段树的方法)
来源:互联网 发布:网络集成实施方案 编辑:程序博客网 时间:2024/06/06 05:20
Problem
Description
Input
Output
Sample Input
5 4
1 2
1 3
3 4
3 5
1 4
2 4
1 2
2 5
Sample Output
3
1
1
2
Data Constraint
Hint
Solution
之前有很多人用了堆来解决这个问题,现在我来讲一下线段树的方法。
我们先按照题意弄出一个优先序列order(就是先进哪个房间)。这个其实很好搞。
数组模拟链表一开始head显示的边是后面加进来的边,那么我们想先遍历的点的编号越小,那么我们可以按照这些边从大到小排,这样的话从head开始搜的边就是最小的,next之后越来越大(因为先加编号大的边,后加编号小的边)。
我们按order建立一棵线段树,来维护每段区间有多少个空位。
先说操作2吧。
对于每一个x,我们要找到它的一个祖先fa,这个祖先fa的父亲是空的。然后x与fa的深度差就是答案。然后fa这个房间空出来。下图是我对这个步骤的解释。
不要忘记将fa要在线段树里面空掉!
关键是操作1。
我们要将order前面x个空的房间补满。我们尽量往左补,如果能够补完就补,不能补完就往右补直到补完为止。
void modify(int s,int l,int r,int x,bool p){ if (l==r) { tree[s]=0; b[o[l]]=1; if (p) mn=l; return; } int wz=(l+r)/2,prd=tree[s*2]; if (prd>=x/*左边有空位*/) modify(s*2,l,wz,x,p); else { modify(s*2,l,wz,prd,0);//将之前的空补满 modify(s*2+1,wz+1,r,x-prd,p);//向右搜 } tree[s]=tree[s*2]+tree[s*2+1];}
至于什么倍增等的基础算法自己去实现。
Code
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#define N 100010#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)#define edge(i,x) for(i=head[x];i;i=next[i])using namespace std;int LCA,ans,x,y,i,j,cnt,n,t,op,tot,mn;int c[N*2][2],o[N],o1[N],deep[N],go[N*2],next[N*2],head[N*2],f[N][20],tree[N*10];bool b[N];void lb(int x,int y){ go[++tot]=y; next[tot]=head[x]; head[x]=tot;}void bs(int x){ int i,now; deep[x]=deep[f[x][0]]+1; fo(i,1,log2(n)) if (f[x][i-1]) f[x][i]=f[f[x][i-1]][i-1]; edge(i,x) { now=go[i]; if (now==f[x][0]) continue; f[now][0]=x; bs(now); } o[++o[0]]=x;}void build(int s,int l,int r){ if (l==r) { tree[s]=1; return; } int wz=(l+r)/2; build(s*2,l,wz); build(s*2+1,wz+1,r); tree[s]=tree[s*2]+tree[s*2+1];}void modify(int s,int l,int r,int x,bool p){ if (l==r) { tree[s]=0; b[o[l]]=1; if (p) mn=l; return; } int wz=(l+r)/2,prd=tree[s*2]; if (prd>=x) modify(s*2,l,wz,x,p); else { modify(s*2,l,wz,prd,0); modify(s*2+1,wz+1,r,x-prd,p); } tree[s]=tree[s*2]+tree[s*2+1];}void change(int s,int l,int r,int x){ if (l==r) { tree[s]=1; return; } if (l==r) return; int wz=(l+r)/2; if (x<=wz) change(s*2,l,wz,x); else change(s*2+1,wz+1,r,x); tree[s]=tree[s*2]+tree[s*2+1]; }void qsort(int l,int r){ int i=l,j=r,m0=c[(l+r)/2][0],m1=c[(l+r)/2][1]; while (i<j) { while (c[i][0]>m0 || c[i][0]==m0 && c[i][1]>m1) i++; while (c[j][0]<m0 || c[j][0]==m0 && c[j][1]<m1) j--; if (i<=j) { swap(c[i],c[j]); i++;j--; } } if (l<j) qsort(l,j); if (i<r) qsort(i,r);}int main(){ scanf("%d%d",&n,&t); fo(i,1,n-1) { scanf("%d%d",&c[i][0],&c[i][1]); if (c[i][0]>c[i][1]) swap(c[i][0],c[i][1]); } qsort(1,n-1); fo(i,1,n-1) lb(c[i][0],c[i][1]),lb(c[i][1],c[i][0]); bs(1); fo(i,1,n) o1[o[i]]=i; build(1,1,n); fo(i,1,t) { scanf("%d%d",&op,&x); if (op==1) { mn=0; modify(1,1,n,x,1); printf("%d\n",o[mn]); } else { LCA=x; fd(j,log2(n),0) if (b[f[LCA][j]]) LCA=f[LCA][j]; b[LCA]=0; ans=deep[x]-deep[LCA]; printf("%d\n",ans); change(1,1,n,o1[LCA]); } }}
3 0
- JZOJ 4811. 排队(线段树的方法)
- jzoj 1278_排队_线段树
- noip2013 火柴排队 线段树
- jzoj1278 排队(线段树)
- 蓝桥杯 小朋友排队(线段树+逆序数的理解)
- 【JZOJ 4811】排队
- jzoj 4811. 【NOIP2016提高A组五校联考1】排队
- [bzoj3333][排队计划][树状数组+线段树]
- 【NOIP2013】火柴排队 线段树+逆序对
- [BZOJ3333]排队计划(线段树)
- BZOJ 2141 排队 线段树套替罪羊
- 蓝桥杯 小朋友排队(线段树)
- 【bzoj3333】排队计划 (线段树)
- JZOJ 4895 三部曲(线段树)
- [JZOJ 2804]【HNOI2012】排队(queue)
- jzoj 1379_【线段树】最大值_线段树
- poj 2828 树状数组or线段树(还原排队的人的序列)
- (jzoj snow的追寻)线段树维护树的直径
- JAVA 中的 StringBuilder 和 StringBuffer 适用的场景是什么?
- aar文件在android studio中的使用
- 车牌识别步骤及部分代码
- if与else同时运行
- Android qq 登录 界面 圆头像
- JZOJ 4811. 排队(线段树的方法)
- arp-tables脚本
- java中自定义异常拦截器
- 程序备忘_速度估计_LV41_speed_cal_and_test
- Linux网络编程
- Linux驱动开发——__stringify
- Android视频学习(九):内容提供者和知识点回顾
- lvs
- 【JZOJ4807】破解