洛谷 P6829 - [IOI2020] 植物比较
比较有意思的一道题,花费了不少时间想出来一个基于拓扑排序的做法,但是不会证明正确性。看了题解以后才恍然大悟。
先从部分分入手思考问题。\(k=2\) 的部分分的做法很 easy,而且看上去没啥启发性,因此我们先思考一下 \(2k>n\) 的部分分怎么做。考虑一个特殊点:\(p_i=n\) 的位置 \(i\),那么容易发现这个位置满足 \(r_i=0\),并且它前面 \(k-1\) 个位置的 \(r_j\ne 0\)。同时我们还可以证明,这样的位置其实是唯一的!,这是因为如果存在另一个 \(r_j=0\),那么必然有 \((i-j)\bmod n\ge k\),而由于 \(2k>n\),所以 \(i\) 必然在 \(j\) 的前 \(k-1\) 个元素中,这样 \(j\) 就不满足“前 \(k-1\) 个元素的 \(r_i\) 非零的限制”。这样一来就好办了,找到符合“\(r_i=0\),且 \(i\) 前 \(k-1\) 个位置的 \(r\) 均非零”的 \(i\),在上面填上 \(n\),然后把前面 \(k-1\) 个位置的 \(r\) 都减去 \(1\) 以后继续处理剩余部分。这样得到的排列是唯一的,直接处理即可。
接下来考虑 \(2k\le n\) 的部分。先考虑构造出一个合法的排列。按照传统艺能,还是找到符合“\(r_i=0\),且 \(i\) 前 \(k-1\) 个位置的 \(r\) 均非零”的 \(i\),在上面填上 \(n\),然后把前面 \(k-1\) 个位置的 \(r\) 都减去 \(1\) 以后继续处理剩余部分。但是棘手的地方在于,这样的 \(i\) 不唯一。怎么办呢?其实方法很简单,随便选一个符合条件的就行了。比较感性的证明是,你发现不管我每次选啥,都不改变任意相邻 \(k\) 个元素的相对大小关系。这样对应的 \(r_i\) 也不变。
这样如果告诉你每次答案都是 \(\pm 1\),问题就很简单了。任选一个符合条件的排列然后比较 \(a_x,a_y\) 大小即可。接下来考虑怎么处理 \(0\) 的情况。既然你限制的是任意相邻 \(k\) 个数的相对大小关系,那我就对于任意相差不超过 \(k-1\) 且 \(a_x>a_y\) 的 \(x,y\) 连条边,然后假设判断出来的 \(a_x>a_y\),我就看看图上是否存在 \(x\to y\) 的路径就行了。但是用传递闭包朴素实现是 \(O(n^3)\) 的。怎么优化呢?类似于找一个代表元的思想,我们对于一个元素 \(x\),分别向顺时针和逆时针方向找到 \(a_y\) 最大的 \(a_y<a_x\) 的 \(y\),然后连边 \(x\to y\),然后你发现一个性质:如果我能从 \(x\) 沿着顺时针方向跳到 \(y\),那么对于 \(x,y\) 中间任何一个 \(a_z<a_x\) 的 \(z\),我在原图上都能从 \(x\) 到 \(z\)“,充分性显然,必要性可以反证法说明,大概就如果到不了,那么必然中间卡在某一个位置走不出去了,这样就无法延伸到更远的位置。
这样一来问题就容易很多了,先求出一个合法排列,然后回答询问时候假设 \(a_x>a_y\),那么我们向左向右从 \(x\) 开始倍增,看能否覆盖 \(y\),如果能就是 \(\pm 1\),不能就是 \(0\)。求出合法排列可以用线段树维护 \(r_i\) 和前 \(k-1\) 个元素里有多少 \(r_i=0\) 的位置,这样总复杂度是单 log。
太晚了,代码今早起来一定补上。
upd:鸽完了。
const int MAXN=2e5;
const int LOG_N=20;
const int INF=0x3f3f3f3f;
int n,m,c[MAXN+5],cnt[MAXN+5],p[MAXN*3+5];
namespace S1{
struct node{int l,r,val,cnt,ok,lz_val,lz_cnt;}s[MAXN*4+5];
void pushup(int k){
s[k].val=min(s[k<<1].val,s[k<<1|1].val);
s[k].cnt=min(s[k<<1].cnt,s[k<<1|1].cnt);
s[k].ok=min(s[k<<1].ok,s[k<<1|1].ok);
}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;s[k].ok=INF;if(l==r)return s[k].val=c[l],void();
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
s[k].val=min(s[k<<1].val,s[k<<1|1].val);
}
void tag(int k,int v1,int v2){
s[k].val+=v1;s[k].cnt+=v2;s[k].ok+=v2;
s[k].lz_val+=v1;s[k].lz_cnt+=v2;
}
void pushdown(int k){
if(s[k].lz_val||s[k].lz_cnt){
tag(k<<1,s[k].lz_val,s[k].lz_cnt);
tag(k<<1|1,s[k].lz_val,s[k].lz_cnt);
s[k].lz_val=s[k].lz_cnt=0;
}
}
void modify_val(int k,int l,int r,int v){
if(l<=s[k].l&&s[k].r<=r)return tag(k,v,0),void();
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid)modify_val(k<<1,l,r,v);
else if(l>mid)modify_val(k<<1|1,l,r,v);
else modify_val(k<<1,l,mid,v),modify_val(k<<1|1,mid+1,r,v);
pushup(k);
}
void modify_cnt(int k,int l,int r,int v){
if(l<=s[k].l&&s[k].r<=r)return tag(k,0,v),void();
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid)modify_cnt(k<<1,l,r,v);
else if(l>mid)modify_cnt(k<<1|1,l,r,v);
else modify_cnt(k<<1,l,mid,v),modify_cnt(k<<1|1,mid+1,r,v);
pushup(k);
}
void banpos(int k,int p){
if(s[k].l==s[k].r)return s[k].ok=INF,void();
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(p<=mid)banpos(k<<1,p);else banpos(k<<1|1,p);
pushup(k);
}
vector<int>vec;
void setok(int k,int l,int r){
if(s[k].val)return;
if(s[k].l==s[k].r)return s[k].ok=s[k].cnt,vec.pb(s[k].l),void();
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid)setok(k<<1,l,r);
else if(l>mid)setok(k<<1|1,l,r);
else setok(k<<1,l,mid),setok(k<<1|1,mid+1,r);
pushup(k);
}
int findpos(int k){
if(s[k].l==s[k].r)return s[k].l;pushdown(k);
if(!s[k<<1].ok)return findpos(k<<1);
else return findpos(k<<1|1);
}
}
void addcnt(int pos,int coef){
int L=(pos+1)%n,R=(pos+m-1)%n;
if(L<=R)S1::modify_cnt(1,L,R,coef);
else S1::modify_cnt(1,L,n-1,coef),S1::modify_cnt(1,0,R,coef);
}
int pos[MAXN+5],toL[MAXN*3+5][LOG_N+2],toR[MAXN*3+5][LOG_N+2];
namespace S2{
struct node{int l,r,mx;}s[MAXN*4+5];
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;s[k].mx=-1;if(l==r)return;
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void modify(int k,int p,int x){
if(s[k].l==s[k].r)return s[k].mx=x,void();
int mid=s[k].l+s[k].r>>1;
if(p<=mid)modify(k<<1,p,x);else modify(k<<1|1,p,x);
s[k].mx=max(s[k<<1].mx,s[k<<1|1].mx);
}
int query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r)return s[k].mx;
int mid=s[k].l+s[k].r>>1;
if(r<=mid)return query(k<<1,l,r);
else if(l>mid)return query(k<<1|1,l,r);
else return max(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
}
void init(int K,vector<int>R){
n=R.size();m=K;
for(int i=0;i<n;i++)c[i]=R[i];
S1::build(1,0,n-1);S1::setok(1,0,n-1);for(int x:S1::vec)addcnt(x,1);
for(int i=n;i;i--){
int pos=S1::findpos(1);p[pos]=i;addcnt(pos,-1);
S1::modify_val(1,pos,pos,INF);S1::banpos(1,pos);
int L=(pos-m+1+n)%n,R=(pos-1+n)%n;
S1::vec.clear();
if(L<=R){S1::modify_val(1,L,R,-1);S1::setok(1,L,R);}
else{
S1::modify_val(1,L,n-1,-1);S1::setok(1,L,n-1);
S1::modify_val(1,0,R,-1);S1::setok(1,0,R);
}
for(int x:S1::vec)addcnt(x,1);
}
for(int i=0;i<n;i++)pos[p[i]]=i;
S2::build(1,0,n-1);
for(int i=0;i<3*n;i++)toL[i][0]=toR[i][0]=-2;
for(int i=1;i<=n;i++){
int ps=pos[i],v;
int L=(ps+1)%n,R=(ps+m-1)%n;
if(L<=R)v=S2::query(1,L,R);
else v=max(S2::query(1,L,n-1),S2::query(1,0,R));
if(v!=-1){
v=pos[v];
if(v>ps)toR[ps][0]=v,toR[ps+n][0]=v+n,toR[ps+2*n][0]=v+2*n;
else toR[ps][0]=v+n,toR[ps+n][0]=v+2*n,toR[ps+2*n][0]=3*n;
}
L=(ps-m+1+n)%n;R=(ps-1+n)%n;
if(L<=R)v=S2::query(1,L,R);
else v=max(S2::query(1,L,n-1),S2::query(1,0,R));
if(v!=-1){
v=pos[v];
if(v<ps)toL[ps][0]=v,toL[ps+n][0]=v+n,toL[ps+2*n][0]=v+2*n;
else toL[ps+2*n][0]=v+n,toL[ps+n][0]=v,toL[ps][0]=-1;
}
S2::modify(1,ps,i);
}
for(int i=0;i<n;i++)p[i+n]=p[i+2*n]=p[i];
for(int i=0;i<3*n;i++)if(toR[i][0]==-2)toR[i][0]=i;
for(int i=0;i<3*n;i++)if(toL[i][0]==-2)toL[i][0]=i;
for(int i=1;i<=LOG_N;i++)for(int j=0;j<3*n;j++){
toL[j][i]=(toL[j][i-1]==-1)?-1:toL[toL[j][i-1]][i-1];
toR[j][i]=(toR[j][i-1]==3*n)?(3*n):toR[toR[j][i-1]][i-1];
}
}
bool checkL(int x,int y){
for(int i=LOG_N;~i;i--){
if(toL[x][i]!=-1&&p[toL[x][i]]>=p[y])x=toL[x][i];
if(x<=y)return 1;
}return 0;
}
bool checkR(int x,int y){
for(int i=LOG_N;~i;i--){
if(toR[x][i]!=3*n&&p[toR[x][i]]>=p[y])x=toR[x][i];
if(x>=y)return 1;
}return 0;
}
int compare_plants(int x,int y){
int rv=1;if(x>y)swap(x,y),rv=-1;
if(p[x]<p[y])return (checkL(y+n,x+n)||checkR(y,x+n))?(-rv):0;
else return (checkR(x+n,y+n)||checkL(x+2*n,y+n))?rv:0;
}
#ifdef LOCAL
int main(){
int n,k,qu;scanf("%d%d%d",&n,&k,&qu);vector<int>r;
for(int i=0,x;i<n;i++)scanf("%d",&x),r.pb(x);
init(k,r);
while(qu--){
int x,y;scanf("%d%d",&x,&y);
printf("%d\n",compare_plants(x,y));
}
return 0;
}
#endif
/*
4 3 2
0 1 1 2
0 2
1 2
*/
/*
4 2 2
0 1 0 1
0 3
1 3
*/

浙公网安备 33010602011771号