zoukankan      html  css  js  c++  java
  • 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_{jle i}(a[j]) > max_{j>i}(a[j])$,我们称$i$和$i+1$的间隔是一个间断,间断把序列分成数段,段和段之间的答案不会相互影响,因为段头可以接在任意一个子序列的末尾

    单个段具有特殊性质!如果这段划分成两个下降子序列,那么划分的方式是唯一的

    证明:

    假设这一段是$[l,r]$,那么只有唯一的一个$xin(l,r],a[x]>a[l]$

    对于$iin (l,r]$

    1.要么存在$lle j<i$,$a[j]<a[i]$,$i$和$j$一定不在一组

    2.要么存在$i<kle 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
    ",ans);
    62     return 0;
    63 }
    View Code
  • 相关阅读:
    班会记录
    CSS之伪元素
    JavaScript之返回顶部
    尝试Hexo
    GitHub之上传文件
    Git之使用
    Git之基本命令
    运行第一个Node.js程序
    go语言圣经 map 章节习题
    go语言圣经第4章JSON部分习题
  • 原文地址:https://www.cnblogs.com/guapisolo/p/14641683.html
Copyright © 2011-2022 走看看