• UOJ564. 【IOI2020】Plants


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

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

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

    保证有解。

    (nle 2*10^5)


    部分分。如果(2k>n),那么排列唯一。考虑从大往小插数,在当前所有没有插数的(h_i=0)的位置中,找到那个(forall jin[i-k+1,i-1],h_j>0)(i)。因为(2k>n)这个(i)是唯一的。将最大值放上去,将(h_j(jin[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
    ",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("
    ");
    		*/
    		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("
    ");
    		for (int i=0;i<n+n;++i)
    			printf("%d ",suc[i][0]);
    		printf("
    ");
    		*/
    		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);
    }
    
  • 相关阅读:
    前端异常上报
    前端异常解析:Source Map
    前端操作剪切板不完全指北
    多系统之间模块相互引用的引发的深思
    浅谈vue原理(四)
    浅谈vue原理(三)
    浅谈vue原理(二)
    浅谈vue原理(一)
    vue中路由嵌套的作用
    常用的学习网站和小工具
  • 原文地址:https://www.cnblogs.com/jz-597/p/14983771.html
Copyright © 2020-2023  润新知