UOJ564. 【IOI2020】Plants

有权值\(h_i\)(排列)。一个环上,\(r_i\)表示从\(i\)开始顺时针往后\(k-1\)个中有多少个权值大于\(h_i\)

给出\(r_i\)\(k\),然后\(Q\)次询问每次问\(x,y\)询问其大小关系。

大小关系即:在所有可能的\(h_i\)中,如果大小关系确定就输出其大小关系;否则输出其大小关系不确定。

保证有解。

\(n\le 2*10^5\)


部分分。如果\(2k>n\),那么排列唯一。考虑从大往小插数,在当前所有没有插数的\(h_i=0\)的位置中,找到那个\(\forall j\in[i-k+1,i-1],h_j>0\)\(i\)。因为\(2k>n\)这个\(i\)是唯一的。将最大值放上去,将\(h_j(j\in[i-k+1,i-1])\)减一,继续做。

没有这个限制时,有可能找出多个这样的\(i\)

结论1:如果有解,只要任取一个这样的\(i\),继续做下去,一定可以构造出个合法解。

证明:

万能归纳:只需要证明任取这样的\(i\)之后还会有这样的\(i\)。如果当前这样的\(i\)唯一,肯定是取\(i\);如果有多个,操作了其中一个\(i\)之后,之前存在的另一个\(i\)仍然满足条件,所以还是存在。证毕。

感觉很有道理但是太简单感觉不对劲?这个证明确实是错的(

结论有个前提是“如果有解”,而上面证明中后面这个情况中,我并没有保证有解。

考虑把最大值填到任选的这个\(i\)之后,剩下的值按照原解的相对大小顺序填。这样就构造出了另一个解。

结论2:对于任意解,对于任意\(i,j,|i-j|<k\)\(i\)\(j\)的大小关系相同。

证明:

考虑分析上面填数的过程。

每次填进去的都是\(\pm k-1\)范围内未填的数的最大值,这就相当于固定了他们的大小关系。

先找到任意一个解。用点数据结构随便做。

暴力可以直接连边,然后看看询问点之间谁能到谁。

实际上只需要记个\(pre_i,suc_i\)表示往前后至多\(k-1\)距离内大于\(h_i\)\(h_j\)最小的位置\(j\)。求出之后倍增即可。


#include "plants.h"

using namespace std;
#include <bits/stdc++.h>
namespace Mine{
	#define mp make_pair
	#define fi first
	#define se second
	const int N=200005,INF=1000000000;
	int n,K;
	int f[N];
	struct info{
		int mn,tag;
		int lp,rp,one;
	} s[N*4];
	void upd(int k){
		s[k].mn=min(s[k<<1].mn,s[k<<1|1].mn);
		s[k].lp=(s[k<<1].lp!=-1?s[k<<1].lp:s[k<<1|1].lp);
		s[k].rp=(s[k<<1|1].rp!=-1?s[k<<1|1].rp:s[k<<1].rp);
		s[k].one=(s[k<<1].one!=-1?s[k<<1].one
				:s[k<<1|1].one!=-1?s[k<<1|1].one
				:s[k<<1].rp!=-1 && s[k<<1|1].lp!=-1 && s[k<<1|1].lp-s[k<<1].rp>=K?s[k<<1|1].lp
				:-1);
	}
	void pd(int k){
		if (s[k].tag){
			s[k<<1].mn+=s[k].tag,s[k<<1].tag+=s[k].tag;
			s[k<<1|1].mn+=s[k].tag,s[k<<1|1].tag+=s[k].tag;
			s[k].tag=0;
		}
	}
	void build(int k=1,int l=0,int r=n-1){
		if (l==r){
			s[k].mn=f[l];
			s[k].lp=s[k].rp=(f[l]==0?l:-1);
			s[k].one=-1;
			return;
		}
		int md=l+r>>1;
		build(k<<1,l,md);
		build(k<<1|1,md+1,r);
		upd(k);
	}
	void era(int x,int k=1,int l=0,int r=n-1){
		if (l==r){
			s[k].mn=INF;
			s[k].lp=s[k].rp=-1;
			return;
		}
		pd(k);
		int md=l+r>>1;
		if (x<=md) era(x,k<<1,l,md);
		else era(x,k<<1|1,md+1,r);
		upd(k);
	}
	void find(int k,int l,int r){
		if (s[k].mn>0) return;
		if (l==r){
			s[k].lp=s[k].rp=l;
			return;
		}
		pd(k);
		int md=l+r>>1;
		find(k<<1,l,md);
		find(k<<1|1,md+1,r);
		upd(k);
	}
	void mdf(int st,int en,int k=1,int l=0,int r=n-1){
		if (st<=l && r<=en){
			s[k].tag--;
			s[k].mn--;
			find(k,l,r);
			return;
		}
		pd(k);
		int md=l+r>>1;
		if (st<=md) mdf(st,en,k<<1,l,md);
		if (md<en) mdf(st,en,k<<1|1,md+1,r);
		upd(k);
	}
	int p[N*2];
	int pre[N*2][19],suc[N*2][19],null;
	set<pair<int,int> > st;
	void init(int _k,vector<int> &_r){
		n=_r.size();
		K=_k;
		for (int i=0;i<n;++i)
			f[i]=_r[i];
		build();
		for (int i=n-1;i>=0;--i){
			int x=(s[1].one!=-1?s[1].one:s[1].lp);
			//printf("%d\n",x);
			p[x]=i;
			era(x);
			if (x-K+1>=0)
				mdf(x-K+1,x-1);
			else{
				mdf(x-K+1+n,n-1);
				if (x)
					mdf(0,x-1);
			}
		}
		/*
		for (int i=0;i<n;++i)
			printf("%d ",p[i]);
		printf("\n");
		*/
		for (int i=0;i<n;++i)
			p[i+n]=p[i];
		null=n+n;
		st.clear();
		for (int i=0;i<n+n;++i){
			if (i-K>=0)
				st.erase(mp(p[i-K],i-K));
			auto iter=st.upper_bound(mp(p[i],i));
			pre[i][0]=(iter!=st.end()?iter->se:null);
			st.insert(mp(p[i],i));
		}
		st.clear();
		for (int i=n+n-1;i>=0;--i){
			if (i+K<n+n)
				st.erase(mp(p[i+K],i+K));
			auto iter=st.upper_bound(mp(p[i],i));
			suc[i][0]=(iter!=st.end()?iter->se:null);
			st.insert(mp(p[i],i));
		}
		/*
		for (int i=0;i<n+n;++i)
			printf("%d ",pre[i][0]);
		printf("\n");
		for (int i=0;i<n+n;++i)
			printf("%d ",suc[i][0]);
		printf("\n");
		*/
		pre[null][0]=suc[null][0]=null;
		for (int j=1;j<=18;++j)
			for (int i=0;i<=n+n;++i){
				pre[i][j]=pre[pre[i][j-1]][j-1];
				suc[i][j]=suc[suc[i][j-1]][j-1];
			}
	}
	int qry_pre(int x,int y){
		if (x-y<K) return p[x]<p[y];
		for (int i=18;i>=0;--i)
			if (pre[x][i]!=null && y+K<=pre[x][i])
				x=pre[x][i];
		x=pre[x][0];
		return x!=null && x-y<K && p[x]<p[y];
	}
	int qry_suc(int x,int y){
		if (y-x<K) return p[x]<p[y];
		for (int i=18;i>=0;--i)
			if (suc[x][i]!=null && y-K>=suc[x][i])
				x=suc[x][i];
		x=suc[x][0];
		return x!=null && y-x<K && p[x]<p[y];
	}
	int ask(int x,int y){
		int o=1;
		if (x>y) o*=-1,swap(x,y);
		int xy=(qry_suc(x,y)||qry_pre(x+n,y));
		int yx=(qry_pre(y,x)||qry_suc(y,x+n));
		return o*(xy?-1:yx?1:0);
	}
}
void init(int k, std::vector<int> r){
	Mine::init(k,r);
}
int compare_plants(int x, int y){
	return Mine::ask(x,y);
}
posted @ 2021-07-07 21:48  jz_597  阅读(59)  评论(0编辑  收藏  举报