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]];
}