原文链接https://www.cnblogs.com/zhouzhendong/p/HDU5470.html

题目传送门 - HDU5470

题意

  你需要写一个只包含小写字母的字符串 $s$ 。

  你有两种操作:

  1. 在当前写好的字符串的末尾加上一个字符 $c$ ,代价是 $cost_c$ ,所有的 $cost_c$ 都会给出。

  2. 在已经写好的字符串中,选择一段子串 $a$ ,复制,并粘贴一次。代价是 $A\times |a|+B\times 2$ 。其中 $A$ 和 $B$ 会给出。

  问你写完字符串的最少花费是多少。

  多组数据,$T\leq 100,|s|\leq 10^5,\sum |s| \leq 1.2\times 10^6,\ \ 1\leq A,B,cost_c\leq 10^9$

题解

  首先,我们看到这种涉及子串和末尾加上字符的题目,自然想到 SAM ,所以先建一个 SAM 再说。

  我们令 $dp_i$ 表示写完前 $i$ 个字符的最小花费。则每一个 $dp_i$ 有两种转移方式,一种是直接加字符,一种是复制粘贴。直接加字符的转移很容易,但是复制粘贴的转移方案很多。

  我们考虑一个 $dp_i$ 通过复制粘贴对后面的 $dp$ 值的贡献。有一个很显然的结论:它所能贡献的区域一定是一个连续段。考虑从第 $i+1$ 个位置开始取串,在由第 $i$ 个前缀构成的 SAM 上面走,得到能匹配的最长串,则比它短的显然都能匹配。我们再分析一下贡献的性质:贡献为 串长 × A + 2 × B 。是一个等差数列,我们显然可以线段树搞一下。

  现在我们回到之前,考虑如何求那个区间。区间下界显然是 $0$ ,那么问题在求区间上界,以第 $i$ 个字符为左端点,开始与第 $i-1$ 个前缀的子串匹配,我们记最远匹配点为 $R_i$ 。首先我们预处理一下每一个节点的 $right$ 集合中最小的位置,设为 $Time_i$,这样只要走到的节点 $x$ 满足 $Time_x<y$ ,我们就可以在第 $y-1$ 个前缀找到相应的匹配串。再有一个显然的结论,$R_i$ 是单调不降的。因为只需要截掉当前匹配串的第一个字母,就可以转移到下一个位置的匹配,而且是一定匹配的。所以我们可以考虑双指针单调扫一遍,把所有的 $R_i$ 都算出来。

  既然 $R_i$ 是单调的,而且我们需要区间更新 min 的等差数列数列的公差都是 $A$ ,所以我们可以考虑单调队列优化一下,这样就不需要写线段树了。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100005;
int T,n,Case=0;
int R[N],c[26],A,B;
int Time[N<<1],tax[N],id[N<<1];
char s[N];
LL dp[N],v[N],q[N],head,tail;
struct SAM{
int Next[26],fa,Max;
}t[N<<1];
int size;
void init(){
memset(t,0,sizeof t);
memset(Time,63,sizeof Time);
size=1,t[0].Max=-1;
for (int i=0;i<26;i++)
t[0].Next[i]=1;
}
int extend(int p,int c){
if (t[p].Next[c]&&t[p].Max+1==t[t[p].Next[c]].Max)
return t[p].Next[c];
int np=++size,q,nq;
t[np].Max=t[p].Max+1;
for (;!t[p].Next[c];p=t[p].fa)
t[p].Next[c]=np;
q=t[p].Next[c];
if (t[p].Max+1==t[q].Max)
t[np].fa=q;
else {
nq=++size;
t[nq]=t[q],t[nq].Max=t[p].Max+1;
t[np].fa=t[q].fa=nq;
for (;t[p].Next[c]==q;p=t[p].fa)
t[p].Next[c]=nq;
}
return np;
}
void Get_Time(){
memset(tax,0,sizeof tax);
for (int i=1;i<=size;i++)
tax[t[i].Max]++;
for (int i=1;i<=n;i++)
tax[i]+=tax[i-1];
for (int i=1;i<=size;i++)
id[tax[t[i].Max]--]=i;
for (int i=size;i>1;i--)
Time[t[id[i]].fa]=min(Time[t[id[i]].fa],Time[id[i]]);
}
int main(){
scanf("%d",&T);
while (T--){
scanf("%s",s+1);
n=strlen(s+1);
init();
for (int i=1,p=1;i<=n;i++)
Time[p=extend(p,s[i]-'a')]=i;
Get_Time();
for (int i=0;i<26;i++)
scanf("%d",&c[i]);
scanf("%d%d",&A,&B);
for (int i=1,j=0,p=1,len=0;i<=n;i++){
while (1){
int Next=t[p].Next[s[j+1]-'a'];
while (!(Next&&Time[Next]<i)&&j-t[t[p].fa].Max<i)
p=t[p].fa,len=t[p].Max,Next=t[p].Next[s[j+1]-'a'];
if (Next&&Time[Next]<i)
j++,p=Next;
else
break;
}
R[i]=j;
}
head=1,tail=0;
memset(q,0,sizeof q);
dp[0]=0;
for (int i=1;i<=n;i++){
dp[i]=dp[i-1]+c[s[i]-'a'];
while (head<=tail&&R[q[head]+1]<i)
head++;
if (head<=tail)
dp[i]=min(dp[i],v[q[head]]+1LL*A*i+B*2);
v[i]=dp[i]-1LL*A*i;
if (R[i+1]>i){
while (head<=tail&&v[i]<=v[q[tail]])
tail--;
q[++tail]=i;
}
}
printf("Case #%d: %lld\n",++Case,dp[n]);
}
return 0;
}

  

HDU5470 Typewriter SAM 动态规划 单调队列的更多相关文章

  1. Luogu4022 CTSC2012熟悉的文章(广义后缀自动机+二分答案+动态规划+单调队列)

    对作文库中的串建出广义SAM,然后显然可以二分答案,二分之后考虑暴力dp,设f[i]为前i位最长匹配长度,显然有f[i]=max(f[i-1],f[j]+i-j) (i-j>=l&&am ...

  2. P4022-[CTSC2012]熟悉的文章【广义SAM,dp,单调队列】

    正题 题目链接:https://www.luogu.com.cn/problem/P4022 题目大意 给出\(m\)个模板串. 然后\(n\)次询问给出一个串\(S\)要求找到一个最大的\(L\)使 ...

  3. BZOJ_1096_[ZJOI2007]_仓库建设_(斜率优化动态规划+单调队列+特殊的前缀和技巧)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1096 有\(n\)个工厂,给出第\(i\)个工厂的到1号工厂的距离\(x[i]\),货物数量\ ...

  4. BZOJ_1010_[HNOI2008]_玩具装箱toy_(斜率优化动态规划+单调队列)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1010 给出\(n\)和\(l\).有\(n\)个玩具,第\(i\)个玩具的长度是\(c[i]\ ...

  5. UOJ#172. 【WC2016】论战捆竹竿 字符串 KMP 动态规划 单调队列 背包

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ172.html 题解 首先,这个问题显然是个背包问题. 然后,可以证明:一个字符串的 border 长度可 ...

  6. 【洛谷】【动态规划+单调队列】P1714 切蛋糕

    [题目描述:] 今天是小Z的生日,同学们为他带来了一块蛋糕.这块蛋糕是一个长方体,被用不同色彩分成了N个相同的小块,每小块都有对应的幸运值. 小Z作为寿星,自然希望吃到的第一块蛋糕的幸运值总和最大,但 ...

  7. BZOJ2442 Usaco2011 Open修剪草坪(动态规划+单调队列)

    显然可以dp.显然可以单调队列优化一下. #include<iostream> #include<cstdio> #include<cmath> #include& ...

  8. 【洛谷】【动态规划+单调队列】P1725 琪露诺

    [题目描述:] 在幻想乡,琪露诺是以笨蛋闻名的冰之妖精. 某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来.但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸.于是琪露诺决定到河 ...

  9. HDU5470 Typewriter (SAM+单调队列优化DP)

    Typewriter Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Tota ...

随机推荐

  1. PHPExcel导出数据时字段超过26列出错Invalid cell coordinate [1

    http://blog.csdn.net/dl425134845/article/details/46650961 以下是解决方案函数 /** *   方法名:    getExcel *   作用  ...

  2. Laravel 5.2分页--怎么在一个页面实现两个以上的列表分页,互不影响?

    今天就碰到这样的一个问题?想在一个页面里面放两个列表,并且两个列表都可以进行分页. 但是,laravel提供的分页方法很方便,可是两个以上就出问题了,当我点其中一个分页的链接时候,页面上其余的分页跟着 ...

  3. Codeforces 280D k-Maximum Subsequence Sum [模拟费用流,线段树]

    洛谷 Codeforces bzoj1,bzoj2 这可真是一道n倍经验题呢-- 思路 我首先想到了DP,然后矩阵,然后线段树,然后T飞-- 搜了题解之后发现是模拟费用流. 直接维护选k个子段时的最优 ...

  4. Confluence 6 选择一个默认的语言

    管理员可以设置应用到你 Confluence 站点所有空间的默认语言.请注意,一个独立的用户可以在他们自己的属性中选择他们独立的语言属性. 设定默认的语言 在 Confluence 站点中修改默认的语 ...

  5. Confluence 6 访问你的宏正文(body)

    请查看 Writing User Macros 页面获得有关如何写用户宏的介绍. 这个页面介绍你可以在用户宏中可以使用的的代码信息. 访问你的宏正文(body) 在你用户宏模板中的 $body 对象可 ...

  6. jQuery获取地址栏中的链接参数

    http://caibaojian.com/177.html 问题描述 今天做一个主题,有一个需求是根据不同的页面来做,虽然php也可以做到,不过考虑到自己的特效代码都是在jQuery上完成,想着能否 ...

  7. django 中session的存储和获取

  8. 反向找related_name以及limit_fields_to

    问题2:客户的添加页面,通过popup创建用户时 解决方案: 如果新创建的用户时:如果是销售部的人,页面才增加 目的是:拿到limit_choices_to,就可以判断了 当有两个Foreignkey ...

  9. this作用范围

    1. this的指向 var name='window';var obj={ name:'obj', say:function(){ return function(){ return this.na ...

  10. bzoj 1856

    做这题之前先看道高考真题(好像是真题,我记不清了) 例:已知一个由n个0和n个1排列而成的数列,要求对于任意k∈N*且k∈[1,2n],在前k个数中1的个数不少于0的个数,求当n=4时这样的数列的数量 ...