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=;
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) 贪心正确性证明的更多相关文章
- bzoj1640[Usaco2007 Nov]Best Cow Line 队列变换*&&bzoj1692[Usaco2007 Dec]队列变换*
bzoj1640[Usaco2007 Nov]Best Cow Line 队列变换 bzoj1692[Usaco2007 Dec]队列变换 题意: 有一个奶牛队列.每次可以在原来队列的首端或是尾端牵出 ...
- [bzoj1692][Usaco2007 Dec]队列变换_后缀数组_贪心
队列变换 bzoj-1692 Usaco-2007 Dec 题目大意:给定一个长度为$n$的字符串.每次从头或尾取出一个字符加到另一个字符串里.要求变换后生成的字符串字典序最小,求字典序最小的字符串. ...
- 【BZOJ-1692&1640】队列变换 后缀数组 + 贪心
1692: [Usaco2007 Dec]队列变换 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 1153 Solved: 482[Submit][St ...
- BZOJ1692: [Usaco2007 Dec]队列变换
1692: [Usaco2007 Dec]队列变换 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 594 Solved: 246[Submit][Sta ...
- [bzoj1692] [Usaco2007 Dec]队列变换 (hash||暴力)
本题同bzoj1640...双倍经验双倍幸福 虽然数据范围n=3w然而O(n²)毫无压力= = http://blog.csdn.net/xueyifan1993/article/details/77 ...
- BZOJ4278 : [ONTAK2015]Tasowanie
首先在串的末尾加上1000,然后进行归并,每次取字典序较小的那个后缀即可. 用hash+二分支持查询lcp,时间复杂度$O(n\log n)$. #include<cstdio> type ...
- [bzoj1692][Usaco2007 Dec]队列变换——贪心+后缀数组
Brief Description 给定一个数列,您每次可以把数列的最前面的数或最后面的数移动到新数列的开头,使得新数列字典序最小.输出这个新序列. Algorithm Design 首先我们可以使用 ...
- BZOJ4278 [ONTAK2015]Tasowanie[后缀数组+贪心]
题目 求两数组归并后的数组最小字典序排列. 嘛,可能本人在贪心这块还是太弱了(或者说什么都弱),如果不知道是字符串题估计也想不起来用sa. 显然看得出归并时字典序小的那个数组先往里面加,这就是要比较两 ...
- [BZOJ4278] [ONTAK2015]Tasowanie 贪心+后缀数组
题目链接 最近做题目好像有点东一榔头西一棒.好吧其实订正模拟题的时候需要用到什么感觉不太熟的就写一下吧. 显然直接贪心,比较两个点后面的串的字典序,小就选谁就可以了. 可以把两个串接起来,加一个\(i ...
随机推荐
- 北京Uber优步司机奖励政策(3月14日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- 【LG3527】[POI2011]MET-Meteors
[LG3527][POI2011]MET-Meteors 题面 洛谷 题解 整体二分. 每次二分\(mid\),如果到时间\(mid\)以收集过\(P_i\)就存入子序列\(L\),否则存入子序列\( ...
- Android Stadio配置了gralde的本地路径,但是windos 命令行还是会下载gradle
如下图: 已经在stadio 里面设置了gradle 的路径,但是在cmd 命令行里面不会去用这个路径. 解决方案:需要在环境变量里面设置一个gradle home GRADLE_USER_HOME ...
- (转)EDM邮件制作规范完整版
转载:http://www.maildesign.cn/archives/1380 在我们的日常工作中,经常需要发送邮件和我们的会员沟通.如注册确认.营销推广等.这些由站方发给会员的信件,往往纯文本格 ...
- Filter配置多个url-pattern
java开发中会用的Filter过滤器,有时候开发需要,在一个Filter中需要配置多个过滤地址,即<url-pattern>,下面就说一下一个Filter过滤器中多个<url-pa ...
- 编写你自己的Python模块
其实网上Python教程挺多的,编写你自己的模块很简单,这其实就是你一直在做的事情!这是因为每一个 Python 程序同时也是一个模块.你只需要保证它以 .py 为扩展名即可.下面的案例会作出清晰的解 ...
- OIDC in Angular 6
参照 草根专栏- ASP.NET Core + Ng6 实战:https://v.qq.com/x/page/i07702h18nz.html 1. OIDC-Client https://githu ...
- Python入门(5)
导览: 函数 集合 迭代器与生成器 模块 一.函数 只要学过其他编程语言应该对函数不太陌生,函数在面向过程的编程语言中占据了极重要的地位,可以说没有函数,就没有面向过程编程,而在面向对象语言中,对象的 ...
- Python入门(3)
一.列表 列表是用来储存和处理多个数据的数据类型,我们可以像下面这样来创建一个列表: my_list = [1, 2, 3] 列表和数学中的集合很像,但是,列表中的数据是可以重复,并且他们是有序的,列 ...
- solidity事件详解
很多同学对Solidity 中的Event有疑问,这篇文章就来详细的看看Solidity 中Event到底有什么用? 写在前面 Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊. ...