LOJ3364. 「IOI2020」植物比较 [思维]

成功拿到 LOJ 上除 HeRaNO 的一血,爽啦。

然而常数是他的四倍,代码长度是他的两倍,还调sb错误调了一上午

T2 是个简单构造,T3 是个模拟费用流(裸题?),就不写了。

思路

先盯着这个 \(2k>n\) 看,它有什么性质呢?

发现 \(2k>n\) 时排列是唯一的:从大到小确定每个数的位置,那么 \(n\) 一定在 \(r=0\) 的位置,而 \(r=0\) 的位置显然会在一个劣弧上(不然就无解了),所以 \(n\) 只能在一个端点上。确定 \(n\) 后给它前面的 \(r\) 减一,继续操作。

更严谨地说,每次是挑出一个 0 ,使得前一个 0 和它的距离超过 \(k\) ,把当前的最大值放在那里。

然而这个结论显然不能扩展到 \(k\) 小的情况,因为这时候可能有很多合法的 0 。

性质一:每次任取一个合法的 0 填入最大值,一定可以构造出一个合法的排列。

证明:已知存在一个合法的排列。在某个时刻,可以把还没填数的位置按照在合法排列中的大小顺序从大到小填入剩下的数,那么可以发现(反证)每次填的位置都是一个合法的 0 。

暴力构造排列是 \(O(n^2)\) 的,但可以用线段树+set+双堆做到 \(O(n\log n)\)

性质二:一个排列合法等价于每连续 \(k\) 个数之间的大小关系与构造出的那个排列相同。

证明:同样可以反证。

根据条件可以得到一个 DAG ,那么 \((x,y)\) 的大小关系能够确定当且仅当 \(x\) 能到达 \(y\) (或反过来)。然而暴力连边跑传递闭包显然不太行。

设连边是小向大连,那么可以发现一个点 \(x\) 其实只需要连两条边,分别向 \((x-K,x-1],[x+1,x+K)\) 里面比 \(x\) 大的最小的点连边。判断的时候就是分别往两个方向跳,看跳到 \(y\) 旁边的时候是否还比 \(y\) 小。

由于一个方向只有一条边,所以倍增即可。

代码

丑死了。

#include<bits/stdc++.h>
#include "plants.h"
clock_t t=clock();
namespace my_std{
	using namespace std;
	#define pii pair<int,int>
	#define fir first
	#define sec second
	#define MP make_pair
	#define rep(i,x,y) for (int i=(x);i<=(y);i++)
	#define drep(i,x,y) for (int i=(x);i>=(y);i--)
	#define go(x) for (int i=head[x];i;i=edge[i].nxt)
	#define templ template<typename T>
	#define sz 402020
	typedef long long ll;
	typedef double db;
	mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
	templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
	templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
	templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
	templ inline void read(T& t)
	{
		t=0;char f=0,ch=getchar();double d=0.1;
		while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
		while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
		if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
		t=(f?-t:t);
	}
	template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
	char __sr[1<<21],__z[20];int __C=-1,__zz=0;
	inline void Ot(){fwrite(__sr,1,__C+1,stdout),__C=-1;}
	inline void print(register int x)
	{
		if(__C>1<<20)Ot();if(x<0)__sr[++__C]='-',x=-x;
		while(__z[++__zz]=x%10+48,x/=10);
		while(__sr[++__C]=__z[__zz],--__zz);__sr[++__C]='\n';
	}
	void file()
	{
		#ifdef NTFOrz
		freopen("a.in","r",stdin);
		#endif
	}
	inline void chktime()
	{
		#ifdef NTFOrz
		cout<<(clock()-t)/1000.0<<'\n';
		#endif
	}
	#ifdef mod
	ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
	ll inv(ll x){return ksm(x,mod-2);}
	#else
	ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
	#endif
//	inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n,K;
int r[sz],p[sz],id[sz];

namespace SGT
{
	int mn[sz<<2],tag[sz<<2];
	#define ls k<<1
	#define rs k<<1|1
	#define lson ls,l,mid
	#define rson rs,mid+1,r
	void Tag(int k,int w){tag[k]+=w,mn[k]+=w;}
	void pushdown(int k){int &t=tag[k];Tag(ls,t),Tag(rs,t);t=0;}
	void pushup(int k){mn[k]=min(mn[ls],mn[rs]);}
	void modify(int k,int l,int r,int x,int y,int w)
	{
		if (x<=l&&r<=y) return Tag(k,w);
		int mid=(l+r)>>1; pushdown(k);
		if (x<=mid) modify(lson,x,y,w);
		if (y>mid) modify(rson,x,y,w);
		pushup(k); 
	}
	int query(int k,int l,int r)
	{
		if (l==r) return l;
		int mid=(l+r)>>1; pushdown(k);
		if (mn[ls]==0) return query(lson);
		return query(rson);
	}
	int qmin(int k,int l,int r,int x,int y)
	{
		if (x<=l&&r<=y) return mn[k];
		int mid=(l+r)>>1,res=1e9; pushdown(k);
		if (x<=mid) chkmin(res,qmin(lson,x,y));
		if (y>mid) chkmin(res,qmin(rson,x,y));
		return res;
	}
	int query(int k,int l,int r,int x,int y,int w)
	{
		int mid=(l+r)>>1; if (l!=r) pushdown(k);
		if (mn[k]>w) return -1;
		if (x<=l&&r<=y)
		{
			if (l==r) return l;
			if (mn[ls]==w) return query(lson,x,y,w); return query(rson,x,y,w);
		}
		int res=-1;
		if (x<=mid) res=query(lson,x,y,w); if (res!=-1) return res;
		if (y>mid) res=query(rson,x,y,w);
		return res;
	}
	int qry(int l,int r){int w=qmin(1,0,n+n-1,l,r);return w>n?1e9:query(1,0,n+n-1,l,r,w);}
	void build(int k,int l,int r,int tp)
	{
		tag[k]=0;
		if (l==r) return mn[k]=tp?::r[l]:1e9,void();
		int mid=(l+r)>>1;
		build(lson,tp),build(rson,tp); pushup(k);
	}
}
using namespace SGT;

namespace Set_Pq
{
	set<int>s;
	priority_queue<pii>q1,q2;
	void maintain(){while (q1.size()&&q2.size()&&q1.top()==q2.top()) q1.pop(),q2.pop();}
	void insert(int x)
	{
		s.insert(x); auto it=s.find(x);
		if (s.size()==1u) return q1.push(MP(n,x)),void();
		int l,r; 
		if (it==s.begin()) l=*prev(s.end())-n; else l=*prev(it);
		if (next(it)==s.end()) r=*(s.begin())+n; else r=*next(it);
		q2.push(MP(r-l,r%n)); q1.push(MP(r-x,r%n)),q1.push(MP(x-l,x));
	}
	void del(int x)
	{
		auto it=s.find(x);
		if (s.size()==1u) return s.erase(it),q2.push(MP(n,x)),void();
		int l,r; 
		if (it==s.begin()) l=*prev(s.end())-n; else l=*prev(it);
		if (next(it)==s.end()) r=*(s.begin())+n; else r=*next(it);
		q1.push(MP(r-l,r%n)); q2.push(MP(r-x,r%n)),q2.push(MP(x-l,x));
		s.erase(it);
	}
}

int jl[sz][21],jr[sz][21];
int ok(int x,int y){return (x>=0&&x<=n+n-1)?x:y;}
void init(int _k,vector<int>R)
{
	n=R.size(),K=_k; rep(i,0,n-1) r[i]=R[i],p[i]=-1;
	SGT::build(1,0,n-1,1);
	drep(_,n-1,0)
	{
		int x;
		while (mn[1]==0) x=query(1,0,n-1),modify(1,0,n-1,x,x,1e9),Set_Pq::insert(x);
		Set_Pq::maintain();
		x=Set_Pq::q1.top().sec;
		p[x]=_; Set_Pq::del(x); 
		if (x>=K-1) modify(1,0,n-1,x-K+1,x,-1); else modify(1,0,n-1,0,x,-1),modify(1,0,n-1,n+1-K+x,n-1,-1);
	}
	rep(i,0,n-1) id[p[i]]=i; build(1,0,n+n-1,0);
	drep(i,n-1,0)
	{
		int x=id[i];
		int pr=qry(x+1,x+K-1),pl=qry(x+n-K+1,x+n-1)-n;
		jl[x][0]=ok(pl,x),jr[x][0]=ok(pr,x);
		jl[x+n][0]=ok(pl+n,x+n),jr[x+n][0]=ok(pr+n,x+n);
		modify(1,0,n+n-1,x,x,i-1e9),modify(1,0,n+n-1,x+n,x+n,i-1e9);
	}
	rep(j,1,20) rep(i,0,n+n-1) jl[i][j]=jl[jl[i][j-1]][j-1];
	rep(j,1,20) rep(i,0,n+n-1) jr[i][j]=jr[jr[i][j-1]][j-1];
}

int check(int x,int y)
{
	if (x<y) { drep(i,20,0) if (jr[x][i]<=y) x=jr[x][i]; }
	else { drep(i,20,0) if (jl[x][i]>=y) x=jl[x][i]; }
	return abs(x-y)<K&&p[x%n]<=p[y%n];
}
int compare_plants(int x,int y)
{
	if (min((x-y+n)%n,(y-x+n)%n)<K) return p[x]<p[y]?-1:1;
	int _x=x,_y=y;
	if (p[x]>p[y]) swap(x,y);
	if (!check(x,y)&&!check(x+n*(x<y),y+n*(y<x))) return 0;
	return p[_x]<p[_y]?-1:1;
}
posted @ 2020-09-18 14:46  p_b_p_b  阅读(651)  评论(0编辑  收藏  举报