CF1504X Codeforces Round #712

CF1504D Flip the Cards(找規律+貪心)

題目大意:給你n張牌,正反面都有數字,保證所有牌上的數字在$[1,2n]$內且互不相同。你可以翻轉任意張牌,接下來需要把牌按正面的數字從小到大排序,需要保證排序後牌背面的數字是從大到小。給出初始時牌的狀態,問最少需要多少次翻轉才能符合要求,如果一定達不到要求則輸出-1。 n=1e5

限制關係是環,難以從前到後直接處理這個長度爲2n的序列

套路1:把序列砍成一半

我們定義$a[i]$表示數$i$的牌背面的數字是多少。

多畫幾個例子容易發現,如果一張牌正反面都$\le n$或正反面都$>n$,一定不合法!因爲數字各不相同,排的時候剩餘的空不夠!假設有一張牌的數字都$\le n$,那麼在$[1,n]$內可用的其他位置爲$n-2$個,卻還有$n-1$張牌要佔位置。都$>n$的情況是同一個道理

現在只考慮$[1,n]$,我們需要把$a[i]$劃分成兩個下降子序列!第一個序列i正面$a[i]$負面,第二個序列從後到前,a[i]反面i正面。

套路2:觀察性質

如果$i$滿足$min_{j\le i}(a[j]) > max_{j>i}(a[j])$,我們稱$i$和$i+1$的間隔是一個間斷,間斷把序列分成數段,段和段之間的答案不會相互影響,因爲段頭可以接在任意一個子序列的末尾

單個段具有特殊性質!如果這段劃分成兩個下降子序列,那麼劃分的方式是唯一的

證明:

假設這一段是$[l,r]$,那麼只有唯一的一個$x\in(l,r],a[x]>a[l]$

對於$i\in (l,r]$

1.要麼存在$l\le j<i$,$a[j]<a[i]$,$i$和$j$一定不在一組

2.要麼存在$i<k\le r$,$a[k]>a[i]$,$k$和$i$不在一組

對於$(l,x)$的位置,一定和l劃分到一組。在這之後,l開頭一組,x開頭一組。現在需要證明$(x,r]$之間的劃分方式唯一

如果$a[i]$小於l的序列的末尾元素,似乎可以把i分到l或者x的末尾。但它一定不滿足性質1,則性質2成立,它必須和k不在同一組。如果k滿足性質2,對應的位置是k',那麼i,k,k'都不在同一組,一定不合法,因此k只能滿足性質1,與i不在同一組。如果a[i]大於l的末尾元素,則只可能放在x的末尾。

當遍歷到x+1是,l組的末尾(也就是x-1)滿足性質2,x-1對應的k一定不和l一組,只能和x一組。歸納到(x,r]的其他位置,唯一確定了這一段的劃分方式

綜上,我們只需要關心l是正還是反就行了,正反兩種情況討論一下取最小值即可

 1 const int maxn=400000,N1=maxn+5; const int inf=0x3f3f3f3f;
 2 
 3 int n;
 4 template <typename _T> void read(_T &ret)
 5 {
 6     ret=0; _T fh=1; char c=getchar();
 7     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
 8     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
 9     ret=ret*fh;
10 }
11 int oa[N1],ob[N1],a[N1], type[N1];
12 int sma[N1],smi[N1];
13 
14 int mi[2];
15 int check(int l,int r,int p)
16 {
17     mi[p]=a[l]; mi[p^1]=inf; int ans=type[l]^p;
18     for(int i=l+1;i<=r;i++) 
19     {
20         if(a[i]>max(mi[p],mi[p^1])) return -1;
21         if(mi[p]<mi[p^1]){
22             if(a[i]<mi[p]) mi[p]=a[i], ans+=type[i]^p;
23             else mi[p^1]=a[i], ans+=type[i]^p^1;
24         }else{
25             if(a[i]<mi[p^1]) mi[p^1]=a[i], ans+=type[i]^p^1;
26             else mi[p]=a[i], ans+=type[i]^p;
27         }
28     }
29     return ans;
30 }
31 int calc(int l,int r)
32 {
33     return min(check(l,r,1),check(l,r,0));
34 }
35 int solve()
36 {
37     int ans=0, tmp;
38     for(int i=1,j=1;i<=n;i++)
39     {
40         for(j=i;j<n&&smi[j]<=sma[j+1];j++);
41         tmp=calc(i,j); if(tmp==-1) return -1;
42         ans+=tmp;
43         i=j;
44     }
45     return ans;
46 }
47 
48 int main()
49 {
50     scanf("%d",&n);
51     for(int i=1;i<=n;i++)
52     {
53         read(oa[i]), read(ob[i]);
54         if( (oa[i]<=n && ob[i]<=n) || (oa[i]>n && ob[i]>n) ){ puts("-1"); return 0; }
55         if(oa[i]<=n) a[oa[i]]=ob[i]; else a[ob[i]]=oa[i], type[ob[i]]=1;
56     }
57     smi[0]=inf;
58     for(int i=1;i<=n;i++) smi[i]=min(smi[i-1],a[i]);
59     for(int i=n;i>=1;i--) sma[i]=max(sma[i+1],a[i]);
60     int ans=solve();
61     printf("%d\n",ans);
62     return 0;
63 }
View Code

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章