BZOJ 4199: [Noi2015]品酒大会/UOJ #131. 【NOI2015】品酒大会 后缀自动机 树形dp / 后缀数组 单调栈
标签: BZOJ 4199: [Noi2015]品酒大会/UOJ #131. 【NOI2015】品酒大会 后缀自动机 树形dp / 后缀数组 单调栈 Html/CSS博客 51CTO博客
2023-07-07 18:24:22 124浏览
这个题sa sam都可做
曾经作为一名sa选手 BJ还是更倾向用sa
于是先YY了sa 做法 但没写 提供思路 //觉得应该没什么bug
搞完height 单调栈来回扫两边
//想不明白就评个论 BJ涨涨评论/斜眼笑
扫出每个height对应lcp的区间 进行答案更新
第一问
我们都有height对应区间了 当然直接惩罚圆力就好了啊
第二问
不太清楚其他题解讲的并查集是什么鬼。。
还是利用已知的height对应区间
每个区间按height分割成了两半
在区间左半边 右半边 分别找出 maxa mina 之后两对数4种组合就可以更新答案了
//随便什么数据结构就好了啊 线段树啥的
/*
看其他题解写的只用 最大*最大 最小*最小 显然是错的啊。。。
大概是 因为是答案是后缀最大值 单调递增 不小心没卡掉吧。。。
随手卡啊。 {-1,-2} {1,2}
*/
之后后缀自动机做法就很套路了。。
串翻过来 parent tree 就是后缀树
直接树形dp就好了。。。
//代码短 又好YY 当然选择它
听说UOJ的hack数据很有趣1
于是在检查了程序鲁棒性后大力交了一发
本想写下在UOJ光荣AC 结果 这数据可真酸爽 /笑哭
//不过这鬼东西NOI 应该不会有吧。。。
#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void print(ll x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}
const int N=600100,inf=0X3f3f3f3f;
const ll Mn=-4485090715960753727;
int n;
struct SAM
{
int trans[N][26],par[N],mx[N];
int sz,root,suff;
int size[N],V[N];
bool book[N];
int f[N][2];
SAM(){sz=root=suff=1;}
void insert(int x,int val)
{
int p=suff,np=++sz;
size[np]=1;V[np]=val;book[np]=1;
while(p && !trans[p][x])
trans[p][x]=np,p=par[p];
if(!p) par[np]=root;
else
{
int q=trans[p][x];
if(mx[q]==mx[p]+1) par[np]=q;
else
{
int nq=++sz;
mx[nq]=mx[p]+1;
memcpy(trans[nq],trans[q],sizeof(trans[q]));
par[nq]=par[q];
par[q]=par[np]=nq;
while(p && trans[p][x]==q)
trans[p][x]=nq,p=par[p];
}
}
suff=np;
}
void build(char *s,int *a)
{
register int i;
for(i=1;i<=n;++i) insert(s[i]-'a',a[i]);
}
int last[N],ecnt;
struct EDGE{int to,nt;}e[N];
inline void add(int u,int v)
{e[++ecnt]=(EDGE){v,last[u]};last[u]=ecnt;}
ll num[N],MX[N];
void dp(int u)
{
if(book[u]) f[u][0]=f[u][1]=V[u];
for(int i=last[u],v;i;i=e[i].nt)
{
v=e[i].to;
dp(v);
num[mx[u]]+=1ll*size[u]*size[v];
size[u]+=size[v];
if(f[v][0]!=inf)
{
if(f[u][0]!=inf)
MX[mx[u]]=max(MX[mx[u]],1ll*f[u][0]*f[v][0]),
MX[mx[u]]=max(MX[mx[u]],1ll*f[u][1]*f[v][1]),
MX[mx[u]]=max(MX[mx[u]],1ll*f[u][0]*f[v][1]),
MX[mx[u]]=max(MX[mx[u]],1ll*f[u][1]*f[v][0]),
f[u][0]=max(f[u][0],f[v][0]),
f[u][1]=min(f[u][1],f[v][1]);
else f[u][0]=f[v][0],f[u][1]=f[v][1];
}
}
}
void solve()
{
register int i;
for(i=2;i<=sz;++i)
add(par[i],i);
memset(f,0X3f,sizeof(f));
memset(MX,-0X3f,sizeof(MX));
dp(root);
i=n;
while(~(i--)) num[i]+=num[i+1];
i=n;
while(~i && MX[i+1]==Mn) MX[i+1]=0,i--;
while(~i) MX[i]=max(MX[i],MX[i+1]),i--;
for(i=0;i<n;++i)
print(num[i]),putchar(' '),print(MX[i]),putchar('\n');
}
}sam;
int a[N];
char s[N];
int main()
{
n=read();
scanf("%s",s+1);
register int i;
for(i=1;i<=n;++i) a[i]=read();
if(n==1){puts("0 0");return 0;}
reverse(s+1,s+1+n);
reverse(a+1,a+1+n);
sam.build(s,a);
sam.solve();
return 0;
}
好博客就要一起分享哦!分享海报
此处可发布评论
评论(0)展开评论
展开评论
您可能感兴趣的博客