1. 程式人生 > >bzoj3133 [Baltic2013]ballmachine

bzoj3133 [Baltic2013]ballmachine

題目連結:bzoj3133
題目大意:
有一個裝球機器,構造可以看作是一棵樹。有下面兩種操作:
1、從根放入一個球,只要下方有空位,球會沿著樹滾下。如果同時有多個點可以走,那麼會選擇編號最小的節點所在路徑的方向。
2、從某個位置拿走一個球,那麼它上方的球會落下來。

對於每種操作op:
若op = 1,輸出最後一個球落到了哪裡
若op = 2,輸出拿走那個球后有多少個球會掉下來

題解:
線段樹
對於第一個操作,要求每次選擇編號最小的節點所在路徑的方向走,那麼我們就可以知道它們優先順序,即假設全為空的情況下小球下落的位置順序。於是我們可以用一棵線段樹來維護這個序列從左到右第一個為空的位置,即為從根放下一個球它會落到的位置。
又操作一就可以看成從根丟下一個球,重複很多次,那麼答案就是最後一次球落到的位置。

對於操作二的話,拿走一個球后,影響的只是這個位置到根的路徑,容易想到樹剖。答案就是從這個位置到根的路徑有多少個球,這個可以用深度直接求得,因為是鏈狀的。拿走後上面的球會滾下來,於是就相當於拿走了這條路徑上最靠近根的那個球。通過這個來維護狀態。
再開一棵線段樹按樹鏈剖分的dfs序維護從左到右第一個有球的位置,即對於一條路徑來說最靠近根的那個球的位置。

對於狀態的改變,兩棵線段樹要一起維護。
於是那天,我花了一個下午種了兩棵線段樹

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<iostream> #include<algorithm> using namespace std; #define N 405010 struct node { int x,y,next; }a[N];int len,first[N],n,rt; void insert(int x,int y) { len++;a[len].x=x;a[len].y=y; a[len].next=first[x];first[x]=len; } int mp[N],rk[N],ss[N],id; int mymin(int x,int y){return
(x<y)?x:y;} struct px{int x,id;}pt[N]; bool cmp(px x,px y) {return x.x<y.x;} int st[N],ln[N],tot; int siz[N],son[N],fa[N],top[N],dfn[N],fdfn[N],dep[N],cnt; void dfs(int x) { siz[x]=son[x]=0; if (ln[x]) {st[x]=tot;tot+=ln[x];} for (int k=first[x];k!=-1;k=a[k].next) { int y=a[k].y; dep[y]=dep[x]+1;fa[y]=x;dfs(y); if (siz[y]>siz[son[x]]) son[x]=y; siz[x]+=siz[y]; mp[x]=mymin(mp[x],mp[y]); } siz[x]+=1;int tl=0; for (int k=first[x];k!=-1;k=a[k].next) { int y=a[k].y; px ls;ls.id=y;ls.x=mp[y]; pt[st[x]+tl]=ls;tl++; } if (ln[x]) sort(pt+st[x],pt+st[x]+ln[x],cmp); } void get_rk(int x) { if (ln[x]) { for (int i=st[x];i<=st[x]+ln[x]-1;i++) { px now=pt[i]; get_rk(now.id); } }rk[++id]=x;ss[x]=id; } void dfs2(int x,int tp) { dfn[x]=++cnt;fdfn[cnt]=x;top[x]=tp; if (son[x]!=0) dfs2(son[x],tp); for (int k=first[x];k!=-1;k=a[k].next) { int y=a[k].y; if (y==fa[x] || y==son[x]) continue; dfs2(y,y); } } int p[N],c[N]; void bt(int now,int l,int r)//初始 { if (l==r) {p[now]=l;c[now]=0;return;} int mid=(l+r)>>1,lc=now*2,rc=now*2+1; bt(lc,l,mid);bt(rc,mid+1,r); c[now]=0; if (p[lc]!=0) p[now]=p[lc]; else p[now]=p[rc]; } int add(int now,int l,int r)//按順序從左往右找位置放球 { if (l==r) {p[now]=0;return rk[l];} int ret,mid=(l+r)>>1,lc=now*2,rc=now*2+1; if (p[lc]) ret=add(lc,l,mid); else ret=add(rc,mid+1,r); if (p[lc]!=0) p[now]=p[lc]; else p[now]=p[rc]; return ret; } void del(int now,int l,int r,int x)//第x位的球被取走 { if (l==r) {p[now]=x;return;} int ret,mid=(l+r)>>1,lc=now*2,rc=now*2+1; if (x<=mid) del(lc,l,mid,x); else del(rc,mid+1,r,x); if (p[lc]!=0) p[now]=p[lc]; else p[now]=p[rc]; } void ins(int now,int l,int r,int x) { if (l==r) {c[now]=x;return;} int mid=(l+r)>>1,lc=now*2,rc=now*2+1; if (x<=mid) ins(lc,l,mid,x); else ins(rc,mid+1,r,x); if (c[lc]!=0) c[now]=c[lc]; else c[now]=c[rc]; } void cel(int now,int l,int r,int x) { if (l==r) {c[now]=0;return;} int mid=(l+r)>>1,lc=now*2,rc=now*2+1; if (x<=mid) cel(lc,l,mid,x); else cel(rc,mid+1,r,x); if (c[lc]!=0) c[now]=c[lc]; else c[now]=c[rc]; } int ffind(int now,int l,int r,int L,int R) { if (l==L && r==R) return c[now]; int mid=(l+r)>>1,lc=now*2,rc=now*2+1; if (R<=mid) return ffind(lc,l,mid,L,R); if (L>mid) return ffind(rc,mid+1,r,L,R); int ret=ffind(lc,l,mid,L,mid); if (ret==0) ret=ffind(rc,mid+1,r,mid+1,R); return ret; } int get_ans(int x) { int la=0,ret=dep[x]; while (x) { int ls=ffind(1,1,n,dfn[top[x]],dfn[x]); if (!ls && la) break;la=ls; x=fa[top[x]]; } int d=fdfn[la]; cel(1,1,n,dfn[d]); del(1,1,n,ss[d]); return ret-dep[d]; } int main() { //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); int m,i,x,op; scanf("%d%d",&n,&m); len=0;memset(first,-1,sizeof(first)); for (i=1;i<=n;i++) ln[i]=0; for (i=1;i<=n;i++) { scanf("%d",&x); if (x==0) rt=i; else insert(x,i); mp[i]=i;ln[x]++; } tot=1;id=cnt=0;dep[rt]=1; fa[rt]=0;dfs(rt);get_rk(rt); dfs2(rt,rt);bt(1,1,n); for (i=1;i<=m;i++) { scanf("%d%d",&op,&x); if (op==1) { int ans; while (x--) { ans=add(1,1,n); ins(1,1,n,dfn[ans]); } printf("%d\n",ans); }else printf("%d\n",get_ans(x)); } return 0; }