有权值(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);
}