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

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

方便起见,在两个串的最右端都加上很大但不相同的字符,避免第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=;
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=;i<m;++i)sum[i]=;
for(i=;i<n;++i)sum[rk[i]=a[i]]++;
for(i=;i<m;++i)sum[i]+=sum[i-];
for(i=n-;i>=;--i){
sa[--sum[rk[i]]]=i;
}
for(j=,p=;p<n;j<<=,m=p){
for(p=,i=n-j;i<n;++i)res[p++]=i;
for(i=;i<n;++i)if(sa[i]>=j)res[p++]=sa[i]-j;
for(i=;i<n;++i)key[i]=rk[res[i]];
for(i=;i<m;++i)sum[i]=;
for(i=;i<n;++i)sum[rk[i]]++;
for(i=;i<m;++i)sum[i]+=sum[i-];
for(i=n-;i>=;--i)sa[--sum[key[i]]]=res[i];
for(res[sa[]]=,p=,i=;i<n;++i){
if(sa[i]+j<n&&sa[i-]+j<n&&rk[sa[i]]==rk[sa[i-]]&&rk[sa[i]+j]==rk[sa[i-]+j])res[sa[i]]=p-;
else res[sa[i]]=p++;
}
swap(rk,res);
}
for(int i=;i<n;++i)rank[sa[i]]=i;
}
int main(){
int n,m;scanf("%d",&n);
for(int i=;i<n;++i)scanf("%d",&a[i]);
a[n]=;
scanf("%d",&m);
for(int i=;i<=m;++i)scanf("%d",&a[n+i]);
a[n+m+]=;
getsa(n+m+,); int pt1=,pt2=n+;
int lim=n+m;
for(int i=;i<=lim;++i){
printf("%d ",rank[pt1]>rank[pt2]?a[pt2++]:a[pt1++]);
}
return ;
}
#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
const int maxn=;
int tmp[][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[],*res=tmp[];
for(i=;i<m;++i)sum[i]=;
for(i=;i<n;++i)sum[rk[i]=str[i]]++;
for(i=;i<m;++i)sum[i]+=sum[i-];
for(i=n-;i>=;--i)sa[--sum[rk[i]]]=i;
for(j=,p=;p<n;m=p,j<<=){
for(i=;i<m;++i)sum[i]=;
for(p=,i=n-j;i<n;++i)res[p++]=i;
for(i=;i<n;++i)if(sa[i]>=j)res[p++]=sa[i]-j;
for(i=;i<n;++i)sum[key[i]=rk[res[i]]]++;
for(i=;i<m;++i)sum[i]+=sum[i-];
for(i=n-;i>=;--i)sa[--sum[key[i]]]=res[i];
for(res[sa[]]=,p=,i=;i<n;++i){
res[sa[i]]=(rk[sa[i]]==rk[sa[i-]]&&rk[sa[i]+j]==rk[sa[i-]+j])?p-:p++;
}
swap(res,rk);
}
for(i=;i<n;++i)rank[sa[i]]=i;
}
int main(){
int n;scanf("%d",&n);
for(int i=;i<n;++i){
while(str[i]=getchar(),!isgraph(str[i]));
}
for(int i=;i<n;++i)str[n+i+]=str[n-i-];
str[n]='Z'+;str[*n+]='Z'+;
getsa(*n+,);
int pt1=,pt2=n+;
int cnt=;
for(int i=;i<=n;++i){
if(rank[pt1]<rank[pt2]){
printf("%c",str[pt1]);pt1++;
}else{
printf("%c",str[pt2]);pt2++;
}
cnt++;
if(cnt%==)printf("\n");
}
return ;
}

bzoj4278[ONTAK2015]Tasowanie & bzoj1692[USACO 2007Dec]队列变换(Best Cow Line) 贪心正确性证明的更多相关文章

  1. bzoj1640[Usaco2007 Nov]Best Cow Line 队列变换*&&bzoj1692[Usaco2007 Dec]队列变换*

    bzoj1640[Usaco2007 Nov]Best Cow Line 队列变换 bzoj1692[Usaco2007 Dec]队列变换 题意: 有一个奶牛队列.每次可以在原来队列的首端或是尾端牵出 ...

  2. [bzoj1692][Usaco2007 Dec]队列变换_后缀数组_贪心

    队列变换 bzoj-1692 Usaco-2007 Dec 题目大意:给定一个长度为$n$的字符串.每次从头或尾取出一个字符加到另一个字符串里.要求变换后生成的字符串字典序最小,求字典序最小的字符串. ...

  3. 【BZOJ-1692&1640】队列变换 后缀数组 + 贪心

    1692: [Usaco2007 Dec]队列变换 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1153  Solved: 482[Submit][St ...

  4. BZOJ1692: [Usaco2007 Dec]队列变换

    1692: [Usaco2007 Dec]队列变换 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 594  Solved: 246[Submit][Sta ...

  5. [bzoj1692] [Usaco2007 Dec]队列变换 (hash||暴力)

    本题同bzoj1640...双倍经验双倍幸福 虽然数据范围n=3w然而O(n²)毫无压力= = http://blog.csdn.net/xueyifan1993/article/details/77 ...

  6. BZOJ4278 : [ONTAK2015]Tasowanie

    首先在串的末尾加上1000,然后进行归并,每次取字典序较小的那个后缀即可. 用hash+二分支持查询lcp,时间复杂度$O(n\log n)$. #include<cstdio> type ...

  7. [bzoj1692][Usaco2007 Dec]队列变换——贪心+后缀数组

    Brief Description 给定一个数列,您每次可以把数列的最前面的数或最后面的数移动到新数列的开头,使得新数列字典序最小.输出这个新序列. Algorithm Design 首先我们可以使用 ...

  8. BZOJ4278 [ONTAK2015]Tasowanie[后缀数组+贪心]

    题目 求两数组归并后的数组最小字典序排列. 嘛,可能本人在贪心这块还是太弱了(或者说什么都弱),如果不知道是字符串题估计也想不起来用sa. 显然看得出归并时字典序小的那个数组先往里面加,这就是要比较两 ...

  9. [BZOJ4278] [ONTAK2015]Tasowanie 贪心+后缀数组

    题目链接 最近做题目好像有点东一榔头西一棒.好吧其实订正模拟题的时候需要用到什么感觉不太熟的就写一下吧. 显然直接贪心,比较两个点后面的串的字典序,小就选谁就可以了. 可以把两个串接起来,加一个\(i ...

随机推荐

  1. BZOJ1452_Count_KEY

    题目传送门 二维树状数组,对于每个颜色开一个树状数组,用容斥求解. code: #include <cstdio> using namespace std; int read() { ') ...

  2. 《绝地求生大逃杀》BE错误怎么办 BE服务未正常运行及安装失败解决方法

    <绝地求生大逃杀>BattlEye Launcher是游戏的反作弊程序,也是启动过程中做容易出现错误的,今天小编带来“爆锤吧务”分享的<绝地求生大逃杀>BE服务未正常运行及安装 ...

  3. java 流 文件 IO

    Java 流(Stream).文件(File)和IO Java.io 包几乎包含了所有操作输入.输出需要的类.所有这些流类代表了输入源和输出目标. Java.io 包中的流支持很多种格式,比如:基本类 ...

  4. 四分树 (Quadtrees UVA - 297)

    题目描述: 原题:https://vjudge.net/problem/UVA-297 题目思路: 1.依旧是一波DFS建树 //矩阵实现 2.建树过程用1.0来填充表示像素 #include < ...

  5. LeetCode 145 ——二叉树的后序遍历

    1. 题目 2. 解答 2.1. 递归法 定义一个存放树中数据的向量 data,从根节点开始,如果节点不为空,那么 递归得到其左子树的数据向量 temp,将 temp 合并到 data 中去 递归得到 ...

  6. Halcon介绍和下载安装视频教程

    ------------------------Halcon,Visionpro高清视频教程,点击下载视频--------------------------

  7. Python高级编程-多进程

    要让Python程序实现多进程(multiprocessing),我们先了解操作系统的相关知识. Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊.普通的函数调用,调用一次,返回 ...

  8. 【转】jQuery最佳实践

    上周,我整理了<jQuery设计思想>. 那篇文章是一篇入门教程,从设计思想的角度,讲解"怎么使用jQuery".今天的文章则是更进一步,讲解"如何用好jQu ...

  9. nodejs基础学习

    一:复制官网的代码,建立一个简单的服务器 const http = require('http'); const hostname = '127.0.0.1'; const port = 3000; ...

  10. BluetoothClass详解

    一. BluetoothClass简介 1. 继承关系 public final class BluetoothClass extends Object implements Parcelable 该 ...