洛谷 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
*/
posted @ 2023-08-10 01:16  tzc_wk  阅读(93)  评论(0)    收藏  举报