题解 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;
} 
posted @ 2021-02-25 19:39  NuoCarter  阅读(46)  评论(0编辑  收藏  举报