http://codeforces.com/gym/102028/problem/H
题意:叫我们求不同的串的价值,一个串的价值定义为这个串中的最大值。
做法:首先看到不同的串,即顺序数值都不同,关于这个我们直接用后缀数组解决。但不过关于价值怎么求,就是一个问题了。
首先考虑都不同的时候,我们考虑一个每一个位置可以贡献的最大价值,当前位置为i,从左到右,第一个大于等于i的位置pos;
则当前位置的贡献的价值就为sum[pos]+a[i]*(pos-i);具体表达式根据不同写法来求,关于这个怎么求每一个数第一个大于等于他的位置这个,单调栈可以,简单dp也可以。结合上面的那个式子,sum可以预处理求出来。
对于每一个位置如果他没有重合的那么他这个位置对总的价值的贡献就是sum[i];
如果不是的,那么他的贡献就是以他为起点的重合的那部分中找到最大值的位置p加上sum[p] 加上a[p]乘上他到那个位置的距离。
就关于那个一段区间的最大值的位置直接用线段树,或者ST表,建议线段树。
另外n最大有20万,a[i]最大有100万,可以离散化一下。
虽然感觉上不是很难但不过,真正写起来就不那么容易了。真佩服比赛场上能写出来的选手
#include
using namespace std;
typedef long long ll;
const int N=1e6+10;
int sa[N],t1[N],t2[N],c[N],x[N],height[N],n,s[N],rk[N];
void get_sa(int m)
{
int *x=t1,*y=t2;
for(int i=0;i=0;i--) sa[--c[x[i]]]=i;
for(int k=1;k<=n;k<<=1)
{
int p=0;
for(int i=n-k;i=k) y[p++]=sa[i]-k;
for(int i=0;i=0;i--) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;x[sa[0]]=0;
for(int i=1;i=n) break;
m=p;
}
}
void get_height()
{
for(int i=0;ivec;
int getid(int x)
{
int id=lower_bound(vec.begin(),vec.end(),x)-vec.begin()+1;
return id;
}
struct Seg_tree
{
int l,r,x;
int mid(){return (l+r)>>1;}
}st[N<<2];
void build(int o,int l,int r)
{
st[o].l=l,st[o].r=r;
if(l==r){
st[o].x=l;
return;
}
int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
st[o].x=a[st[o<<1].x]>=a[st[o<<1|1].x]?st[o<<1].x:st[o<<1|1].x;
}
int query(int o,int l,int r)
{
if(st[o].l==l&&st[o].r==r)
{
return st[o].x;
}
int mid=st[o].mid();
if(r<=mid) return query(o<<1,l,r);
else if(l>mid) return query(o<<1|1,l,r);
else{
int lm=query(o<<1,l,mid);
int rm=query(o<<1|1,mid+1,r);
return a[lm]>=a[rm]?lm:rm;
}
}
void init()
{
for(int i=0;i<=n+10;i++)
{
height[i]=0;
s[i]=0;
rk[i]=0;
sum[i]=0;
sa[i]=0;
}
vec.clear();
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
init();
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
vec.push_back(a[i]);
}
///离散化
sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end());
for(int i=1;i<=n;i++)
s[i-1]=getid(a[i]);
int len=vec.size();
get_sa(len+1);
get_height();
///处理nxt和sum
nxt[n]=n;
for(int i=n-1;i>=1;i--)
{
int j=i;
while(j=1;i--) sum[i]+=sum[nxt[i]];
build(1,1,n);
ll ans=0;
///处理最大位置
for(int i=1;i<=n;i++)
{
if(height[rk[i-1]]==0) tmp[i]=0;
else tmp[i]=query(1,i,i+height[rk[i-1]]-1);
}
///求和
for(int i=1;i<=n;i++)
{
if(tmp[i]==0) ans+=sum[i];
else ans+=sum[nxt[tmp[i]]]+1LL*a[tmp[i]]*(nxt[tmp[i]]-i-height[rk[i-1]]);
}
printf("%lld\n",ans);
}
return 0;
}
/**
5
6
1 1 1 2 1 2
3
1 2 3
3
2 3 3
4
1 1 1 1
7
3 7 4 3 7 4 3
*/