• bzoj4278[ONTAK2015]Tasowanie & bzoj1692[USACO 2007Dec]队列变换(Best Cow Line) 贪心正确性证明


    做法网上到处都有就不说了.

    这题其实是之前做的….不过由于人太傻现在才想明白比较字典序进行贪心的正确性….

    方便起见,在两个串的最右端都加上很大但不相同的字符,避免第lcp+1个字符不存在的边界。

    如果两个串当前最左端的字符不相同显然选较小的.

    否则,设两个剩下的串的lcp长度为x,那么两个串的第lcp+1个字符(此时必然都存在这个字符,因为我们之前在右端加了很大的哨兵)必然不同.不妨假设第一个串的第lcp+1个字符较小.

    考虑先选第二个串的第1个字符的某种方案.

    如果这种方案中,我们先选了第二个串的第lcp+1个字符再选第一个串的第lcp+1个字符,那么把这种方案在选择第二个串的第lcp+1个字符之前的所有操作中选第一个串的操作改为选第二个串,选第二个串的操作改为选第一个串,最后把选择第二个串的第lcp+1个字符改为选择第一个串的第lcp+1个字符,这样得到先选第一个串的第1个字符且字典序更小的方案.

    如果这种方案中,我们先选了第一个串的第lcp+1个字符,那么第一次操作后第一个串选了0个字符,第二个串选了1个字符.选择第一个串的第lcp+1个字符后,第一个串选了lcp+1个字符,第二个串选了<=lcp个字符.一开始第二个串选的字符多,最后第一个串选的字符多,因为每次只能进行一个操作,中间必然存在某次操作,使得这次操作后两个串选择的字符数目相同.那么我们把这次操作和这次操作之前的操作都反转一下(即:原先这次操作选第一个串,反转后这次操作选第二个串,原先选第二个串,反转后选第一个串),之后的操作不变,就可以得到一个字典序相同但是先选第一个串的第1个字符的方案.

    于是,对于任何一个先选字典序较大的串的方案,我们都可以找到一个至少不会更差的方案先选字典序较小的串.因此最优方案必然是每次选择字典序较小的串.

    某奶牛题poj3623&bzoj1692是从一个字符串两侧拿出字符组成字符串要求字典序最小,同样可以分这样两种情况考虑,由选字典序大的一侧方案得到选字典序小的一侧的方案,且使得最终结果不会更差.两侧开始的串的lcp可能会有重叠部分,拿这个串可能会拿着拿着拿到另一端,但仍然可以进行操作的反转,因此还是可以这么证.

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int maxn=400005;
    int sa[maxn],rank[maxn];
    int tmp1[maxn],tmp2[maxn],key[maxn],sum[maxn];
    int a[maxn];
    void getsa(int n,int m){
      int *rk=tmp1,*res=tmp2,i,j,p;
      for(i=0;i<m;++i)sum[i]=0;
      for(i=0;i<n;++i)sum[rk[i]=a[i]]++;
      for(i=1;i<m;++i)sum[i]+=sum[i-1];
      for(i=n-1;i>=0;--i){
        sa[--sum[rk[i]]]=i;
      }
      for(j=1,p=0;p<n;j<<=1,m=p){
        for(p=0,i=n-j;i<n;++i)res[p++]=i;
        for(i=0;i<n;++i)if(sa[i]>=j)res[p++]=sa[i]-j;
        for(i=0;i<n;++i)key[i]=rk[res[i]];
        for(i=0;i<m;++i)sum[i]=0;
        for(i=0;i<n;++i)sum[rk[i]]++;
        for(i=1;i<m;++i)sum[i]+=sum[i-1];
        for(i=n-1;i>=0;--i)sa[--sum[key[i]]]=res[i];
        for(res[sa[0]]=0,p=1,i=1;i<n;++i){
          if(sa[i]+j<n&&sa[i-1]+j<n&&rk[sa[i]]==rk[sa[i-1]]&&rk[sa[i]+j]==rk[sa[i-1]+j])res[sa[i]]=p-1;
          else res[sa[i]]=p++;
        }
        swap(rk,res);
      }
      for(int i=0;i<n;++i)rank[sa[i]]=i;
    }
    int main(){
      int n,m;scanf("%d",&n);
      for(int i=0;i<n;++i)scanf("%d",&a[i]);
      a[n]=100000;
      scanf("%d",&m);
      for(int i=1;i<=m;++i)scanf("%d",&a[n+i]);
      a[n+m+1]=100000;
      getsa(n+m+2,100001);
      
      int pt1=0,pt2=n+1;
      int lim=n+m;
      for(int i=1;i<=lim;++i){
        printf("%d ",rank[pt1]>rank[pt2]?a[pt2++]:a[pt1++]);
      }
      return 0;
    }
    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    const int maxn=60005;
    int tmp[2][maxn],sum[maxn],key[maxn],sa[maxn],rank[maxn];
    char str[maxn];
    void getsa(int n,int m){
      int i,j,k,p,*rk=tmp[0],*res=tmp[1];
      for(i=0;i<m;++i)sum[i]=0;
      for(i=0;i<n;++i)sum[rk[i]=str[i]]++;
      for(i=1;i<m;++i)sum[i]+=sum[i-1];
      for(i=n-1;i>=0;--i)sa[--sum[rk[i]]]=i;
      for(j=1,p=0;p<n;m=p,j<<=1){
        for(i=0;i<m;++i)sum[i]=0;
        for(p=0,i=n-j;i<n;++i)res[p++]=i;
        for(i=0;i<n;++i)if(sa[i]>=j)res[p++]=sa[i]-j;
        for(i=0;i<n;++i)sum[key[i]=rk[res[i]]]++;
        for(i=1;i<m;++i)sum[i]+=sum[i-1];
        for(i=n-1;i>=0;--i)sa[--sum[key[i]]]=res[i];
        for(res[sa[0]]=0,p=1,i=1;i<n;++i){
          res[sa[i]]=(rk[sa[i]]==rk[sa[i-1]]&&rk[sa[i]+j]==rk[sa[i-1]+j])?p-1:p++;
        }
        swap(res,rk);
      }
      for(i=0;i<n;++i)rank[sa[i]]=i;
    }
    int main(){
      int n;scanf("%d",&n);
      for(int i=0;i<n;++i){
        while(str[i]=getchar(),!isgraph(str[i]));
      }
      for(int i=0;i<n;++i)str[n+i+1]=str[n-i-1];
      str[n]='Z'+1;str[2*n+1]='Z'+2;
      getsa(2*n+2,256);
      int pt1=0,pt2=n+1;
      int cnt=0;
      for(int i=1;i<=n;++i){
        if(rank[pt1]<rank[pt2]){
          printf("%c",str[pt1]);pt1++;
        }else{
          printf("%c",str[pt2]);pt2++;
        }
        cnt++;
        if(cnt%80==0)printf("
    ");
      }
      return 0;
    }
  • 相关阅读:
    介绍一个小工具 Linqer
    wcf系列5天速成——第一天 binding的使用(1)
    wcf系列5天速成——第二天 binding的使用(2)
    wcf系列学习5天速成——第三天 事务的使用
    iptables 使用
    rsync 文件.数据同步
    Nginx打开目录浏览功能
    linux 添加开机启动
    watch 命令
    python 命令行处理
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6478318.html
Copyright © 2020-2023  润新知