Loading

P6829 [IOI2020]植物比较

P6829 [IOI2020]植物比较

听WC2021的时候听到了这道题,然后就来做了。

大概思路就是处理出一个类似拓扑序的东西。如果一个数后面 \(k\) 个数中比它大的数都已经扩展过了,那么就扩展它。

每次取出一个 \(0\) 扩展,用线段树区间减动态来维护每个数后面比它小的数的个数(有多个 \(0\) 取哪个并不那么容易解决,后面会讲)。

这样可以保证下标差 \(<k\) 的两个数可以通过比较拓扑序大小来比较大小,即拓扑序小的更大。

所以,如果两个数大小关系确定,那么就比较拓扑序(后文写作 \(tpn\))。否则返回 \(0\)

考虑如何判断能否判定大小关系。

不妨 \(tpn_a<tpn_b\) ,那么从 \(a\) 开始,不断往更大的 \(tpn\) 跳,同时维持大小的确定性,如果能跳到 \(b\) ,那么就可以判定大小关系。

维持大小确定性只需要不断往下标差 \(<k\) 的区域跳即可。

可以处理出每个数往左 \(k\) 个数中 \(tpn\) 最小的比 \(a\) 拓扑序大的点,跳到不能跳为止,显然倍增优化一下。往右也要扩展一次。

这个可以按照 \(tpn\) 从大到小加入位置,用线段树维护区间最小值解决。

如果两次至少有一次区域包含了 \(b\) 那么就可以确定大小关系。

然而在实现的时候我遇上的最大的问题是在预处理拓扑序。

如果出现多个 \(0\) ,该取哪个?

直接取下标最小的必然是错的。

考虑如下数据: \(\rm{r=[1,0,1,0,0],k=3}\)

一开始应该找的拓扑序最小的点不应该是 \(2\) 。因为 \(r_5\) 后面三个数,也就是 \([r_5,r_1,r_2]\) ,没有大于 \(r_5\) 的,说明 \(r_1<r_5\)

同理,如果找 \(5\) ,发现 \(r_4\) 后面三个数中间没有大于 \(r_4\) 的,所以 \(r_4>r_5\)

问题应该很明确了:先找到一个 \(0\) ,如果它左边 \(k\) 个之内有 \(0\),那么优先取左边的那个。

剩下就全是实现的问题了。

考虑多开一颗线段树维护所有 \(0\) 的位置,对于每一个区间维护:最靠左的 \(0\) ,最靠右的 \(0\) ,区间相邻的 \(0\) 的最大距离。

同时在原来维护区间减的线段树维护区间最靠前的 \(0\) 的位置,区间最小值。

处理的拓扑序的时候,先在原来的线段树查询出最靠左的 \(0\) 的位置。

如果它可以扩展到 \(n\) (从环的另一端绕),那么再在第二颗线段树上二分端点最靠左的合法后缀,这样子就能找到我们需要的位置了!

复杂度 \(O(n\log n)\)提交记录

#include"plants.h"
#include<bits/stdc++.h>
using namespace std;

#define fi first
#define se second
#define pb push_back
#define mkp make_pair
#define sz(v) (int)(v).size()
template<class T>inline bool ckmax(T&x,T y){return x<y?x=y,1:0;}
template<class T>inline bool ckmin(T&x,T y){return x>y?x=y,1:0;}
#define rep(i,x,y) for(int i=x,i##end=y;i<=i##end;++i)
#define per(i,x,y) for(int i=x,i##end=y;i>=i##end;--i)
typedef long long LL;

const int N=200005;
const int T=N<<2;
const int inf=0x3f3f3f3f;
int n,a[N],tpn[N],tpo,len;
int L[20][N],R[20][N];
LL dl[20][N],dr[20][N];
vector<int>v0;

namespace sg1{
int miv[T],tag[T],id[T];
#define lc (p<<1)
#define rc (p<<1|1)
void pushup(int p){
	miv[p]=inf,id[p]=0;
	if(ckmin(miv[p],miv[lc]))id[p]=id[lc];
	if(ckmin(miv[p],miv[rc]))id[p]=id[rc];
}
void build(int l,int r,int p,int*a){
	tag[p]=0,miv[p]=inf;
	if(l==r)return miv[p]=a[l],id[p]=l,void();
	int mid=(l+r)>>1;
	build(l,mid,lc,a),build(mid+1,r,rc,a);
	pushup(p);
}
void pushdown(int p){
	if(tag[p]){
		tag[lc]+=tag[p],miv[lc]+=tag[p];
		tag[rc]+=tag[p],miv[rc]+=tag[p];
		tag[p]=0;
	}
}
void get0(int p,int l,int r){
	if(l==r)return v0.pb(l),void();
	pushdown(p);
	int mid=(l+r)>>1;
	if(!miv[lc])get0(lc,l,mid);
	if(!miv[rc])get0(rc,mid+1,r);
}
void update(int ql,int qr,int d,int l=1,int r=n,int p=1){
	if(ql>qr)return;
	if(ql<=l&&r<=qr){
		miv[p]+=d,tag[p]+=d;
		if(!miv[p])get0(p,l,r);
		return;
	}
	pushdown(p);
	int mid=(l+r)>>1;
	if(ql<=mid)update(ql,qr,d,l,mid,lc);
	if(mid<qr)update(ql,qr,d,mid+1,r,rc);
	pushup(p);
}
void change(int pos,int d,int l=1,int r=n,int p=1){
	if(l==r)return miv[p]=d,void();
	int mid=(l+r)>>1;
	if(pos<=mid)change(pos,d,l,mid,lc);
	else change(pos,d,mid+1,r,rc);
	pushup(p);
}
pair<int,int>query(int ql,int qr,int l=1,int r=n,int p=1){
	if(ql>qr)return mkp(inf,inf);
	if(ql<=l&&r<=qr)return mkp(miv[p],id[p]);
	int mid=(l+r)>>1;pair<int,int>res=mkp(inf,inf);
	if(ql<=mid)ckmin(res,query(ql,qr,l,mid,lc));
	if(mid<qr)ckmin(res,query(ql,qr,mid+1,r,rc));
	return res;
}
#undef lc
#undef rc
}

namespace sg2{
#define lc (p<<1)
#define rc (p<<1|1)
int sv[T],sl[T],sr[T];
void pushup(int p){
	if(sl[lc])sl[p]=sl[lc];
	else sl[p]=sl[rc];
	if(sr[rc])sr[p]=sr[rc];
	else sr[p]=sr[lc];
	sv[p]=max(sv[lc],sv[rc]);
	if(sl[rc]&&sr[lc])ckmax(sv[p],sl[rc]-sr[lc]);
}
void change(int pos,int k,int l=1,int r=n,int p=1){
	if(l==r){
		sl[p]=sr[p]=k?l:0;
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)change(pos,k,l,mid,lc);
	else change(pos,k,mid+1,r,rc);
	pushup(p);
}
int query(int l=1,int r=n,int p=1){
	if(l==r)return l;
	int mid=(l+r)>>1;
	if(!sl[rc])return query(l,mid,lc);
	if(!sl[lc])return query(mid+1,r,rc);
	if(sv[rc]<len&&sl[rc]-sr[lc]<len)return query(l,mid,lc);
	return query(mid+1,r,rc);
}

#undef lc
#undef rc
}

int dis(int x,int y){
	return x<=y?y-x:y+n-x;
}
int compare_plants(int a,int b){
	++a,++b;
	int flg=1;
	if(tpn[a]>tpn[b])flg=-1,swap(a,b);
	LL dst=0;int u=a;
	for(int i=19;i>=0;--i)
		if(L[i][u]&&tpn[L[i][u]]<=tpn[b])dst+=dl[i][u],u=L[i][u];
	if(dst>=dis(b,a))return flg;
	dst=0,u=a;
	for(int i=19;i>=0;--i)
		if(R[i][u]&&tpn[R[i][u]]<=tpn[b])dst+=dr[i][u],u=R[i][u];
	if(dst>=dis(a,b))return flg;
	return 0;
}
inline bool cmp(const int&a,const int&b){
	return tpn[a]>tpn[b];
}
void init(int k,std::vector<int>r){
	len=k,n=r.size(),tpo=0;
	for(int i=0;i<n;++i)a[i+1]=r[i];
	sg1::build(1,n,1,a);
	for(int i=1;i<=n;++i)if(!a[i])sg2::change(i,1);
	for(int i=1;i<=n;++i){
		int x=sg1::id[1];
		if(x-k+1<1){
			pair<int,int>tmp=sg1::query(n+x-k+1,n);
			if(!tmp.fi)x=sg2::query();
		}
		tpn[x]=++tpo;
		sg1::change(x,inf),sg2::change(x,0);
		v0.clear();
		sg1::update(max(1,x-k+1),x,-1);
		if(x-k+1<1)sg1::update(n+x-k+1,n,-1);
		for(int j:v0)sg2::change(j,1);
	}
	for(int i=1;i<=n;++i)a[i]=inf;
	sg1::build(1,n,1,a);
	for(int i=1;i<=n;++i)a[i]=i;
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;++i){
		int x=a[i],le=0,ri=0;
		pair<int,int>tl=sg1::query(max(1,x-k+1),x);
		if(tl.fi<inf)le=tl.se;
		if(x-k+1<1){
			pair<int,int>ttl=sg1::query(n+x-k+1,n);
			if(tl.fi>ttl.fi)le=ttl.se;
		}
		pair<int,int>tr=sg1::query(x,min(x+k-1,n));
		if(tr.fi<inf)ri=tr.se;
		if(x+k-1>n){
			pair<int,int>ttr=sg1::query(1,x+k-1-n);
			if(tr.fi>ttr.fi)ri=ttr.se;
		}
		sg1::change(x,tpn[x]);
		L[0][x]=le,R[0][x]=ri;
		dl[0][x]=dis(le,x),dr[0][x]=dis(x,ri);
	}
	for(int i=1;i<=19;++i)
		for(int j=1;j<=n;++j)
			L[i][j]=L[i-1][L[i-1][j]],R[i][j]=R[i-1][R[i-1][j]],
			dl[i][j]=dl[i-1][j]+dl[i-1][L[i-1][j]],
			dr[i][j]=dr[i-1][j]+dr[i-1][R[i-1][j]];
}

posted @ 2021-02-04 16:40  zzctommy  阅读(209)  评论(0编辑  收藏  举报