题解 P6753 【[BalticOI 2013 Day1] Ball Machine】
题解 P6753 【[BalticOI 2013 Day1] Ball Machine】
考试硬生生做了 3h....
一个没找出性质的菜鸡。。。
整个思路就是 模拟,但是带有优化
算法:倍增+重链剖分+线段树
对于 1 操作:
可以知道当一个球放在 x 上时,一定是将 x 的子树都放满后再放的 x ,
因为他的儿子是有放的先后之分的,
所以考虑记录下最小编号的路径,再在这条路径上倍增
那么整个一操作就可以分为下面三步:
假设答案为 \(now\)
1.从 \(now\) 倍增跳到最小路径上 dep 最大的点上,使得这个点的子树(包括他自己)的可放置空间 $>= num $
将 \(now\)赋成这个点
2.扫描 \(now\) 的所有出边(按照路径最小编号最小到大,这个可以通过一次树形DP处理出扫描顺序),
如果这个出点 \(y\) 的可放置位置 \(<num\),那么 \(num-y\)子树内剩余的位置,并且将整个 \(y\) 子树的子树变成 1
如果可放置位置\(>=num\)的话,直接使 now=y,并且 break;
3.一直重复这个过程,直到找到一个点使得它的可放置空间 \(= num\)
最后输出 这个点的编号
那么难点就来了,如何快速查找这个点可放置个数?
答案就是轻重链剖分,只需要
query(seg[x],seg[x]+size[x]-1)
就可以获得整个子树有多少个位置已经放置过,
size[x]-query(seg[x],seg[x]+size[x]-1)
就可以快速算出可放置位置了
复杂度\(O(log^2n)\)(看起来常数有点大,但是跑的飞快)
对于 2 操作
很容易想到,这个操作就是从这个点向根走,有多少个连续的 1 。将连续的1的个数 -1(减去自己)就是答案
最后将最上面的 1 删除就好了,这个用树剖就很好判断了.
复杂度因为有一个线段树,所以还是\(O(\log^2n)\)
这道题的细节如果用这个思路的话好多啊。。。
整体复杂度:\(O(n \log^2n)\)
一开始一个树形DP \(O(n)\)
对于一个点 x ,将 x 能到达的点按照这个点的子树最小值从小到大排序,因为一条边只会多出一个点,所以这个复杂度就是\(O(n \log n)\)
倍增复杂度也为\(O(n \log n)\)
Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lc x<<1
#define rc x<<1|1
const int N=1e5+5,Q=1e5+5,LOG=18;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)){f^=ch=='-';ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}
x=f?-x:x;
}
template <typename T>
inline void print(T x){
if(x<0){putchar('-');x=-x; }
if(x>9){print(x/10);}
putchar(x%10+48);
}
int head[N],to[N],minn[N],Next[N],tot,n,q,root;
int fson[LOG][N],len[N];
int dep[N],fa[N],siz[N],son[N];
vector<pair<int,int> > edge[N];
inline bool cmp(pair<int,int> x,pair<int,int> y){return x.first<y.first;}
inline void add(int u,int v){to[++tot]=v;Next[tot]=head[u],head[u]=tot;}
inline void dfs1(int x,int f){
dep[x]=dep[f]+1;siz[x]=1;fa[x]=f;minn[x]=x;
for(register int i=head[x];i;i=Next[i]){
int y=to[i];
dfs1(y,x);
siz[x]+=siz[y];minn[x]=min(minn[x],minn[y]);
edge[x].push_back(make_pair(minn[y],y));
if(siz[y]>siz[son[x]]){son[x]=y;}
}
}
int top[N],seg[N],rev[N],dnt;
inline void dfs2(int x){
top[x]=son[fa[x]]==x? top[fa[x]] : x;
len[top[x]]++;
seg[x]=++dnt;rev[dnt]=x;
if(son[x]){dfs2(son[x]);}
for(register int i=head[x];i;i=Next[i]){
int y=to[i];
if(y!=son[x]){dfs2(y);}
}
}
struct seg_tree{
int l,r,sum,lazy;
#define l(x) c[x].l
#define r(x) c[x].r
#define sum(x) c[x].sum
#define lazy(x) c[x].lazy
}c[N<<2];
inline void update(int x){sum(x)=sum(lc)+sum(rc);}
inline void build(int x,int l,int r){
l(x)=l,r(x)=r;
if(l==r){return;}
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
return;
}
inline void change(int x,int d){sum(x)=(r(x)-l(x)+1);lazy(x)=1;}
inline void push_down(int x){if(lazy(x)){change(lc,lazy(x));change(rc,lazy(x));lazy(x)=0;}}
inline void modify_1(int x,int l,int r,int d){
if(l(x)>=l&&r(x)<=r){change(x,d);return;}
push_down(x);
int mid=(l(x)+r(x))>>1;
if(mid>=l){modify_1(lc,l,r,d);}
if(mid<r){modify_1(rc,l,r,d);}
update(x);
return;
}
inline void modify_2(int x,int l,int d){
if(l(x)==r(x)){sum(x)+=d;return;}
push_down(x);
int mid=(l(x)+r(x))>>1;
if(mid>=l){modify_2(lc,l,d);}
else{modify_2(rc,l,d);}
update(x);
return;
}
inline int query(int x,int l,int r){
if(l(x)>=l&&r(x)<=r){return sum(x);}
push_down(x);
int mid=(l(x)+r(x))>>1;
int res=0;
if(mid>=l){res+=query(lc,l,r);}
if(mid<r){res+=query(rc,l,r);}
return res;
}
inline int uptoroot(int x){
int res=0,tep=0,last=0;
while(top[x]!=root){
tep=query(1,seg[top[x]],seg[x]);
if(tep==seg[x]-seg[top[x]]+1){
res+=tep;
last=top[x];
x=fa[top[x]];
}
else{break;}
}
tep=query(1,seg[top[x]],seg[x]);
if(tep==0){modify_2(1,seg[last],-1);}
else{res+=tep;modify_2(1,seg[x]-tep+1,-1);}
return res-1;
}
int main(){
read(n),read(q);
for(register int i=1;i<=n;++i){
int u;read(u);
if(!u){root=i;}
else{add(u,i);}
}
dfs1(root,0);
dfs2(root);
int lim=log2(n)+1;
for(register int i=1;i<=n;++i){
sort(edge[i].begin(),edge[i].end(),cmp);
if(!edge[i].empty()) fson[0][i]=edge[i][0].second;
}
for(register int j=1;j<=lim;++j){for(register int i=1;i<=n;++i){fson[j][i]=fson[j-1][fson[j-1][i]];}}
build(1,1,n);
while(q--){
int op,num;
read(op),read(num);
if(op==1){
bool flag=false;
int teproot=root;
int now=teproot;
while(!flag){
now=teproot;
if(siz[now]-query(1,seg[now],seg[now]+siz[now]-1)==num){flag=true;break;}
for(register int i=lim;i>=0;--i){
int y=fson[i][now];
if(!y){continue;}
if(siz[y]-query(1,seg[y],seg[y]+siz[y]-1)<num){continue;}
else if(siz[y]-query(1,seg[y],seg[y]+siz[y]-1)==num){flag=true;now=y;break;}
else{now=fson[i][now];}
}
for(register int i=0;i<edge[now].size();++i){
int y=edge[now][i].second;
int tep=query(1,seg[y],seg[y]+siz[y]-1);
if(siz[y]-tep<num){
num-=siz[y]-tep;
modify_1(1,seg[y],seg[y]+siz[y]-1,1);
}
else{teproot=y;break;}
}
}
print(now);putchar('\n');
modify_1(1,seg[now],seg[now]+siz[now]-1,1);
}
else{print(uptoroot(num));putchar('\n');}
}
return 0;
}