JOISC 2024 Day3 T1 : Card Collection / 卡牌收集

云浅知处發表於2024-06-21

首先,注意到對於一組詢問,我們只需要關注每個數與 \((T_j,W_j)\) 的相對大小關係。這一共有 \(9\) 種情況,於是我們直接做區間 DP,設一個形如 \(f(l,r,0/1/2,0/1/2)\) 的狀態,即可得到 \(O(N^3M)\) 的做法;進一步使用 bitset 最佳化可以做到 \(O(\frac{N^3M}{w})\),但是無法透過(甚至 \(N=2000,M=10\) 可能都無法透過)。


我們考慮想要得到一個 \((T_j,W_j)\),不論怎樣,操作總是由兩部分構成:

  • 用區間 \([l,r]\) 造出來一個 \((T_j,W_j)\)
  • 然後無傷通關 \([1,l-1]\)\([r+1,N]\),把這個 \((T_j,W_j)\) 保持到最後。

考慮能夠保持到最後的條件。我們發現,如果某一側全部都形如 \((\ge T_j,<W_j)\),或者全部都形如 \((\le T_j,>W_j)\),那麼一定是不可能保持到最後的。否則,我們證明一定可以。

如果不是全部形如以上兩種形態,那麼相當於至少存在一個 \((\le T_j,\le W_j)\)\((\ge T_j,\ge W_j)\)。我們在這一側一直取 min 或取 max 就可以保留這個 \((\le T_j,\le W_j)\)\((\ge T_j,\ge W_j)\)。最後,進行一次取 min 或取 max 即可。

現在我們考慮,用區間 \([l,r]\) 造出了 \((T_j,W_j)\),如果是直接得到的也就是 \(l=r\),這種情況是好判斷的。

如果這個過程也經歷了至少一次合併,我們設最後一步合併的兩個區間為 \([l,m]+[m+1,r]\),那麼這兩個區間的結果有四種可能:

  • \((T_j,\le W_j)+(\le T_j,W_j)\)
  • \((T_j,\ge W_j)+(\ge T_j,W_j)\)
  • \((\le T_j,W_j)+(T_j,\le W_j)\)
  • \((\ge T_j,W_j)+(T_j,\ge W_j)\)

不管哪種情況,我們發現總是可以將操作改為:先分別合併出 \([l,m]\)\([m+1,r]\),然後把 \([1,l-1]\) 得到的 \((\le T_j,\le W_j)\)\((\ge T_j,\ge W_j)\) 合併到 \([l,m]\) 裡面,並保持形態不變;把 \([r+1,N]\) 得到的 \((\le T_j,\le W_j)\)\((\ge T_j,\ge W_j)\) 合併到 \([m+1,r]\) 裡面,最後再把 \([1,m],[m+1,N]\) 合併。

於是,我們只需要考慮最後一步操作形如 \([1,m]+[m+1,N]\) 的情形。


現在,我們可以把原問題轉化為 \(O(N)\) 次判斷如下的問題:

  • 能否用一個序列造出一個形如 \((T_j,\le W_j)\) 的數。

由對稱性,其他情況也是類似的。

我們考慮想要造出一個 \((T_j,\le W_j)\),發現如果它是由兩個均不為 \((T_j,\le W_j)\) 的卡牌合併而來,那麼唯一的情況就只有 \((\ge T_j,\le W_j)+(T_j,>W_j)\)

我們先來考慮,如何一個序列能否造出 \((\ge T_j,\le W_j)\)。類似地,這個過程也由兩部分組成:

  • 用區間 \([l,r]\) 造出來一個 \((\ge T_j,\le W_j)\)
  • 然後無傷通關 \([1,l-1]\)\([r+1,N]\),把這個 \((\ge T_j,\le W_j)\) 保持到最後。

這時我們發現,\((\ge T_j,\le W_j)\) 不可能由兩個均不為 \((\ge T_j,\le W_j)\) 的卡牌合併而來。於是,唯一的情況只有:原序列中存在至少一個能保留到最後的 \((\ge T_j,\le W_j)\)

現在考慮一個 \((\ge T_j,\le W_j)\) 能保留到最後的條件。發現如果某一側全都是 \((<T_j,>W_j)\) 那麼肯定無解;否則這一側必須存在 \((\ge T_j,*)\) 或者 \((*,\le W_j)\)(其中 \(*\) 表示任取)。對於這種情況,同理我們也可以先在這一邊一直取 min 或 max 保留這個卡牌,最後進行一次操作留下 \((<T_j,>W_j)\)

現在,我們可以在 \(O(N)\) 時間內判斷一個長為 \(N\) 的序列能否造出一個形如 \((\ge T_j,\le W_j)\) 的卡牌。

回到原問題,考慮如何造出一個 \((T_j,\le W_j)\) 的卡牌。下面我們證明,這等價於:

  • 存在至少一個形如 \((T_j,*)\) 的卡牌,且這個序列能造出 \((\ge T_j,\le W_j)\) 的卡牌。

首先,必要性是顯然的。對於充分性,假設我們使用了某個 \(k\) 滿足 \((S_k,V_k)\)(這裡 \(S,V\) 是給定的初始序列)一開始就形如 \((\ge T_j,\le W_j)\),且兩側均存在至少一個 \((\ge T_j,*)\)\((*,\le W_j)\)

\(p\) 是序列中任意一個滿足 \(S_p=T_j\) 的卡牌,分以下兩種情況:

  • \(p=k\)。這種情況下,我們類似地在左右先進行操作,可以發現總是能保留這個 \((T_j,\le W_j)\)
  • \(p\neq k\)。不妨設 \(p<k\),那麼我們在右側正常操作,對於左側,我們無腦保留 \(T_j\)(在不關心第二維的情況下,這當然是可以做到的),然後最後把留下來的 \((T_j,*)\)\((\ge T_j,\le W_j)\) 進行一次合併即可。

綜上命題得證。

於是,我們可以在 \(O(N)\) 的時間內判斷一個長為 \(N\) 的序列能否造出一個形如 \((T_j,\le W_j)\) 的卡牌。對於每組詢問我們都需要做 \(N\) 遍上述過程,於是總的時間複雜度為 \(O(N^2M)\)


下面就是我們熟悉的部分了!相信在得到最後的條件後,作為 CN OIer 你的內心已經蠢蠢欲動了(霧

還是不妨設最後一步合併形如 \((T_j,\le W_j)+(\le T_j,W_j)\)

我們找到第一個形如 \((T_j,*)\) 的卡牌 \(x\),以及第一個形如 \((\ge T_j,\le W_j)\) 且左側存在至少一個 \((\ge T_j,*)\)\((*,\le W_j)\),或者左側沒有任何卡牌的卡牌 \(y\)。找到 \(y\) 右側第一個形如 \((\ge T_j,*)\)\((*,\le W_j)\) 的卡牌 \(z\),那麼能合成 \((T_j,\le W_j)\) 的字首只有 \([1,y]\)(此時還需要 \(x\le y\)),以及所有的 \([1,p]\),其中 \(p\ge \max(x,z)\)

同理我們也可以找到所有的字尾使得其能構造出 \((\le T_j,W_j)\)。現在相當於給出 \(p_1,q_1,p_2,q_2\),判斷是否存在一個 \(i\) 滿足 \(i\in \{p_1\}\cup[q_1,n],i+1\in \{p_2\}\cup [1,p_1]\)。分四種情況討論即可。

對於找到這個字首,可以發現只需要每組詢問只需要做一次「查詢 \(p\) 之後第一個 \((\ge u,\le v)\) 的卡牌的位置」這樣的詢問,還有兩次查詢 \((\ge u,*)\)\((*,\le v)\) 的詢問。後兩個都容易透過線段樹二分或 ST 表解決,對於第一個,我們把詢問按 \(u\) 排序後掃描線,那麼只需要線段樹二分同時維護單點修改即可。

最後還需要考慮直接拿著序列中一個 \((T_j,W_j)\) 走到最後的情形。考慮把詢問記憶化,每次列舉序列中的所有 \((T_j,W_j)\) 並一一判斷其前後是否分別都有一個 \((\ge T_j,\ge W_j)\)\((\le T_j,\le W_j)\)。注意這並不是三維偏序,因為我們只需要判斷存在性。同理按照 \(T\) 從小到大插入,每次線段樹二分即可。

綜上,本題在 \(O((N+M)\log N)\) 時間內解決。帶有 \(8\) 倍常數,因為有四種情況且每次都要對字首字尾同時算。

#include<bits/stdc++.h>

#define ll long long
#define mk make_pair
#define fi first
#define se second

using namespace std;

inline int read(){
	int x=0,f=1;char c=getchar();
	for(;(c<'0'||c>'9');c=getchar()){if(c=='-')f=-1;}
	for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15);
	return x*f;
}

template<typename T>void cmax(T &x,T v){x=max(x,v);}
template<typename T>void cmin(T &x,T v){x=min(x,v);}

const int N=4e5+5;
int n,m,a[N],b[N],val[N],qx[N],qy[N];
vector<pair<int,int> >ques[N];
bool ans[N];
int st[N],ed[N];

struct sgt{
	int mx[N<<2],mn[N<<2];
	#define ls(p) (p<<1)
	#define rs(p) (p<<1|1)
	void pushup(int p){mx[p]=max(mx[ls(p)],mx[rs(p)]),mn[p]=min(mn[ls(p)],mn[rs(p)]);}
	void build(int l,int r,int p){
		if(l==r)return mn[p]=mx[p]=val[l],void();
		int mid=(l+r)>>1;
		build(l,mid,ls(p)),build(mid+1,r,rs(p)),pushup(p);
	}
	int geq(int l,int r,int v,int ql,int qr,int p){ // min i in [l,r] s.t. val[i]>=v
		if(mx[p]<v||l>r)return n+1;
		if(ql==qr)return ql;
		int mid=(ql+qr)>>1,ret=n+1;
		if(l<=mid)ret=geq(l,r,v,ql,mid,ls(p));
		if(ret!=n+1)return ret;
		return geq(l,r,v,mid+1,qr,rs(p));
	}
	int leq(int l,int r,int v,int ql,int qr,int p){ // min i in [l,r] s.t. val[i]<=v
		if(mn[p]>v||l>r)return n+1;
		if(ql==qr)return ql;
		int mid=(ql+qr)>>1,ret=n+1;
		if(l<=mid)ret=leq(l,r,v,ql,mid,ls(p));
		if(ret!=n+1)return ret;
		return leq(l,r,v,mid+1,qr,rs(p));
	}
	void mdf(int x,int v,int ql,int qr,int p){
		if(ql==qr)return mx[p]=mn[p]=v,void();
		int mid=(ql+qr)>>1;
		if(x<=mid)mdf(x,v,ql,mid,ls(p));
		else mdf(x,v,mid+1,qr,rs(p));
		pushup(p);
	}
}T1,T2,T3;

int pos[N],V,pv[N],q1[N],q2[N],qq[N];
vector<int>vals[N];
void solve1(){
	for(int i=1;i<=n;i++)val[i]=a[i];T1.build(1,n,1);
	for(int i=1;i<=n;i++)val[i]=b[i];T2.build(1,n,1);
	for(int i=1;i<=n;i++)val[i]=V+1;T3.build(1,n,1);
	
	for(int i=1;i<=V;i++)pv[i]=n+1,vector<int>().swap(vals[i]);
	for(int i=1;i<=n;i++)cmin(pv[a[i]],i),vals[a[i]].emplace_back(i);

	for(int u=V;u>=1;u--){
		for(int j:vals[u])T3.mdf(j,b[j],1,n,1);
		for(auto [v,id]:ques[u]){
			int p=0;
			if(a[1]>=u&&b[1]<=v)p=1;
			else{
				p=min(T1.geq(1,n,u,1,n,1),T2.leq(1,n,v,1,n,1));
				if(p>n){pos[id]=n+1,qq[id]=0;continue;}
				p=T3.leq(p+1,n,v,1,n,1);
				if(p>n){pos[id]=n+1,qq[id]=0;continue;}
			}
			int q=min(T1.geq(p+1,n,u,1,n,1),T2.leq(p+1,n,v,1,n,1));
			pos[id]=max(q,pv[u]);
			if(pv[u]<=p)qq[id]=p;
			else qq[id]=0;
		}
	}
}

void solve(){
	for(int i=1;i<=V;i++)vector<pair<int,int> >().swap(ques[i]);
	for(int i=1;i<=m;i++)ques[qx[i]].emplace_back(mk(qy[i],i));
	solve1();
	for(int i=1;i<=m;i++)st[i]=pos[i],q1[i]=qq[i];

	reverse(a+1,a+n+1),reverse(b+1,b+n+1);
	for(int i=1;i<=n;i++)swap(a[i],b[i]);for(int i=1;i<=m;i++)swap(qx[i],qy[i]);
	
	for(int i=1;i<=V;i++)vector<pair<int,int> >().swap(ques[i]);
	for(int i=1;i<=m;i++)ques[qx[i]].emplace_back(mk(qy[i],i));
	solve1();
	for(int i=1;i<=m;i++)ed[i]=n-pos[i]+1,q2[i]=n-qq[i]+1;

	for(int i=1;i<=m;i++){
		ans[i]|=(st[i]<ed[i]);
		if(q1[i]!=0)ans[i]|=(q1[i]<ed[i]);
		if(q2[i]!=n+1)ans[i]|=(st[i]<q2[i]);
		if(q1[i]!=0&&q2[i]!=n+1)ans[i]|=(q1[i]==q2[i]-1);
	}
	
	reverse(a+1,a+n+1),reverse(b+1,b+n+1);
	for(int i=1;i<=n;i++)swap(a[i],b[i]);for(int i=1;i<=m;i++)swap(qx[i],qy[i]);
}

bool s1[N],s2[N],t1[N],t2[N],can[N];
map<int,vector<int> >Map[N];
map<int,bool>res[N];

void solve_case1(){
	vector<vector<int> >ps(V+1);
	for(int i=1;i<=n;i++)ps[a[i]].emplace_back(i);
	for(int i=1;i<=n;i++)val[i]=n+1;T1.build(1,n,1);
	for(int i=1;i<=V;i++){
		for(int j:ps[i])T1.mdf(j,b[j],1,n,1);
		for(int j:ps[i])s1[j]=(T1.leq(1,n,b[j],1,n,1)<=j-1);
	}

	for(int i=1;i<=n;i++)val[i]=-1;T1.build(1,n,1);
	for(int i=V;i>=1;i--){
		for(int j:ps[i])T1.mdf(j,b[j],1,n,1);
		for(int j:ps[i])s2[j]=(T1.geq(1,n,b[j],1,n,1)<=j-1);
	}
	for(int i=1;i<=V;i++)ps[i].clear();

	reverse(a+1,a+n+1),reverse(b+1,b+n+1);
	for(int i=1;i<=n;i++)ps[a[i]].emplace_back(i);
	for(int i=1;i<=n;i++)val[i]=n+1;T1.build(1,n,1);
	for(int i=1;i<=V;i++){
		for(int j:ps[i])T1.mdf(j,b[j],1,n,1);
		for(int j:ps[i])t1[j]=(T1.leq(1,n,b[j],1,n,1)<=j-1);
	}

	for(int i=1;i<=n;i++)val[i]=-1;T1.build(1,n,1);
	for(int i=V;i>=1;i--){
		for(int j:ps[i])T1.mdf(j,b[j],1,n,1);
		for(int j:ps[i])t2[j]=(T1.geq(1,n,b[j],1,n,1)<=j-1);
	}
	for(int i=1;i<=V;i++)ps[i].clear();

	reverse(t1+1,t1+n+1),reverse(t2+1,t2+n+1),reverse(a+1,a+n+1),reverse(b+1,b+n+1);
	for(int i=1;i<=n;i++)can[i]=((s1[i]|s2[i]|(i==1))&(t1[i]|t2[i]|(i==n)));
	for(int i=1;i<=V;i++)Map[a[i]][b[i]].emplace_back(i);

	for(int i=1;i<=m;i++){
		if(res[qx[i]].find(qy[i])!=res[qx[i]].end()){ans[i]|=res[qx[i]][qy[i]];continue;}
		for(int j:Map[qx[i]][qy[i]])if(can[j]){ans[i]=res[qx[i]][qy[i]]=1;break;}
	}
}

signed main(void){

	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read(),b[i]=read();
	for(int i=1;i<=m;i++)qx[i]=read(),qy[i]=read();

	vector<int>lsh(n+m);
	for(int i=1;i<=n;i++)lsh[i-1]=a[i];
	for(int i=1;i<=m;i++)lsh[i+n-1]=qx[i];
	sort(lsh.begin(),lsh.end());
	int V1=unique(lsh.begin(),lsh.end())-lsh.begin();lsh.resize(V1);
	for(int i=1;i<=n;i++)a[i]=lower_bound(lsh.begin(),lsh.end(),a[i])-lsh.begin()+1;
	for(int i=1;i<=m;i++)qx[i]=lower_bound(lsh.begin(),lsh.end(),qx[i])-lsh.begin()+1;

	lsh.resize(n+m);	
	for(int i=1;i<=n;i++)lsh[i-1]=b[i];
	for(int i=1;i<=m;i++)lsh[i+n-1]=qy[i];
	sort(lsh.begin(),lsh.end());
	int V2=unique(lsh.begin(),lsh.end())-lsh.begin();lsh.resize(V2);
	for(int i=1;i<=n;i++)b[i]=lower_bound(lsh.begin(),lsh.end(),b[i])-lsh.begin()+1;
	for(int i=1;i<=m;i++)qy[i]=lower_bound(lsh.begin(),lsh.end(),qy[i])-lsh.begin()+1;
	V=max(V1,V2);

	solve_case1();

	solve();

	for(int i=1;i<=n;i++)swap(a[i],b[i]);
	for(int i=1;i<=m;i++)swap(qx[i],qy[i]);
	solve();

	for(int i=1;i<=n;i++)a[i]=V-a[i]+1,b[i]=V-b[i]+1;
	for(int i=1;i<=m;i++)qx[i]=V-qx[i]+1,qy[i]=V-qy[i]+1;
	solve();

	for(int i=1;i<=n;i++)swap(a[i],b[i]);
	for(int i=1;i<=m;i++)swap(qx[i],qy[i]);
	solve();

	for(int i=1;i<=m;i++)if(ans[i])cout<<i<<" ";puts("");

	return 0;
}

相關文章