您的位置:首页 > 其它

bzoj 3790: 神奇项链 manachar+dp+树状数组

2017-05-31 19:50 351 查看

题意

母亲节就要到了,小 H 准备送给她一个特殊的项链。这个项链可以看作一个用小写字

母组成的字符串,每个小写字母表示一种颜色。为了制作这个项链,小 H 购买了两个机器。第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠。例如:aba和aca连接起来,可以生成串abaaca或 abaca。现在给出目标项链的样式,询问你需要使用第二个机器多少次才能生成这个特殊的项链。

T<=5,n<=50000

分析

把模型化简一下就变成了每次可以用一个回文串来覆盖这个字符串的某一段,问最少需要多少次才能把整个字符串覆盖。

很显然如果我们选择了一个不是极大的回文串(两边仍然可以拓展),那么我们选了极大的回文串一定不会使得答案更差。

所以就可以用manachar把本质不同的n*2个极大回文串求出来,然后用数据结构优化一下dp就好了。

一开始是想用线段树的,查了一下发现原来可以树状数组,只要反过来就好了。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;

const int N=50005;

int n,ch[N*2],len[N*2],c
,f
;
struct data{int l,r;}a[N*2];
char s
;

void manachar()
{
for (int i=1;i<=n;i++) ch[i*2]=s[i]-'a',ch[i*2-1]=100;
ch[n*2+1]=100;
int pos=0,mx=0;
for (int i=1;i<=n*2+1;i++)
{
if (mx>i) len[i]=min(mx-i+1,len[pos*2-i]);
else len[i]=1;
while (i+len[i]<=n*2+1&&i-len[i]>=1&&ch[i+len[i]]==ch[i-len[i]]) len[i]++;
if (i+len[i]-1>mx) mx=i+len[i]-1,pos=i;
}
}

bool cmp(data a,data b)
{
return a.r<b.r;
}

void ins(int x,int y)
{
while (x<=n+1)
{
c[x]=min(c[x],y);
x+=x&(-x);
}
}

int query(int x)
{
int ans=inf;
while (x)
{
ans=min(ans,c[x]);
x-=x&(-x);
}
return ans;
}

int main()
{
while (scanf("%s",s+1)!=EOF)
{
n=strlen(s+1);
manachar();
int a1=0;
for (int i=2;i<=n*2;i++)
{
a[++a1].l=(i-len[i]+2)/2;
a[a1].r=(i+len[i]-1)/2;
if (a[a1].l>a[a1].r) a1--;
}
sort(a+1,a+a1+1,cmp);
memset(c,inf,sizeof(c));
ins(n+1,0);
for (int i=1,j=1;i<=n;i++)
{
f[i]=inf;
while (j<=a1&&a[j].r==i) f[i]=min(f[i],query(n-a[j].l+2)+1),j++;
if (f[i]<inf) ins(n-i+1,f[i]);
}
printf("%d\n",f
-1);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: