Luogu P6852 Mex|構造
阿新 • • 發佈:2020-11-23
題目大意:
有一個 \(0\) 到 \(n\) 的排列,下標從 \(0\) 開始,給出 \(m\) 個限制,為 \(l\) 到 \(r\) 的 \(mex\) 為 \(val\),構造這個排列使得滿足限制,或判斷無解。
\(1 \le n,m\le 5\times 10^5\)
題目思路:
提供一種碼量相對不大的方法,目前不開O2最優解第二頁。
首先對於每個限制,都意味著 \([0,val-1]\) 都必須在該區間內,\(val\) 必須不在該區間內。特別的,\(val=0\) 意味著 \(0\) 不在該區間內。
因為若 \(val\) 被限定在區間 \([l,r]\) 內,則 \(val-1\)
因此,我們應考慮將數從前往後放置,並維護可放數的位置和不能放置的位置。對於\(0\)的放置,因可放置區間不連續,需要特判。對於 \([1,n]\),列舉可放置位置,若找到一個合法空位,則放置即可。若遇到不可放置區間(顯然也是連續的),則整段跳過。
按照上文所述的性質,這樣放置不會導致無解(除非原本就無解)。因此,除非無解,這個方法一定可以構造合法方案。
現在可得 \(45\) 分。考慮優化。程式時間使用最多的是查詢未使用的位置。使用並查集維護每個位置的狀況,每個連通塊的最後一個位置為未使用
由於原來每個連通塊都是未被使用,合併後每個聯通塊仍舊有一個位置未被使用。且按照上文的合併方法,因為合併時前面指向後面,因此連通塊的根節點一定是該塊的最後一個位置,亦即未使用的位置。
優化後,查詢未使用的位置時間大大減少,可以通過本題。
上程式碼
#include<bits/stdc++.h> using namespace std; int n,m,pl[500100],pr[500100],ql[500010],qr[500010],z[500100]; int l,r,val,q[500100];int fa[500100]; int getfa(int x) { if (fa[x]==x) return x; return fa[x]=getfa(fa[x]); } void hebing(int x,int y) { x=getfa(x);y=getfa(y); fa[x]=y; } int main() { //p陣列維護可放位置,q陣列維護不可放位置 scanf("%d%d",&n,&m); for (int i=0;i<=n;i++) pr[i]=n,ql[i]=n+1; for (int i=1;i<=m;i++) { scanf("%d%d%d",&l,&r,&val); ql[val]=min(ql[val],l);qr[val]=max(qr[val],r); if (!val) z[l]++,z[r+1]--; else { pl[val-1]=max(pl[val-1],l),pr[val-1]=min(pr[val-1],r); } } for (int i=n-1;i>=0;i--) { pl[i]=max(pl[i],pl[i+1]); pr[i]=min(pr[i],pr[i+1]); }//p求字尾最大值和最小值 for (int i=1;i<=n;i++) fa[i]=i; for (int i=0;i<=n;i++)//特判0的情況 { if (i) z[i]+=z[i-1]; if (pl[0]<=i&&pr[0]>=i&&!z[i]) {q[i]=0;hebing(i,i+1);break;} if (i==n) { cout<<"-1\n"; return 0; }//無法尋找到可放置的空位,即無解。下同 } for (int i=1;i<=n;i++) { bool ok=false; for (int j=pl[i];j<=pr[i];j++) { j=getfa(j);//直接找到空位 if (j>=ql[i]&&j<=qr[i]) {j=qr[i];continue;}//是否進入不合法區間 if (j<=pr[i]) {q[j]=i;hebing(j,getfa(j)+1);ok=true;break;}//找到空位 } if (!ok) { cout<<"-1\n"; return 0; } } for (int i=0;i<=n;i++) cout<<q[i]<<" "; return 0; }
PS:一開始 \(val\) 不在該區間內這一限制被我忽略了導致WA了好久