2018-2019 ACM-ICPC, Asia Jiaozuo H - Can You Solve the Harder Problem?

Portal:GYM-102028H

题意:
求所有本质不同子序列的最大值的和。

思路:
参考本质不同的子串做法处理本质不同子序列:后缀数组, h e i g h t height height部分的子串就是没有贡献的子串。
考虑以位置 i i i 为左端点的所有子串答案的求法,找到 i i i 后第一个大于 a [ i ] a[i] a[i] 的位置 P o s Pos Pos s u m [ i ] = a [ i ] ∗ ( P o s − i ) + s u m [ P o s ] sum[i]=a[i]*(Pos-i)+sum[Pos] sum[i]=a[i](Posi)+sum[Pos]
用单调栈可求 i i i 右侧第一个大于 a [ i ] a[i] a[i] 的位置 N e x t [ i ] Next[i] Next[i] s u m sum sum 数组也可预处理出来。
答案的统计:从 1 − n 1-n 1n遍历,若 h e i g h t [ r a n k [ i ] ] height[rank[i]] height[rank[i]] 0 0 0 , 则当前位置对答案的贡献就是 s u m [ i ] sum[i] sum[i] 。若不为 0 0 0 ,用线段树等方法求出 i i i i + h e i g h t [ r a n k [ i ] ] − 1 i+height[rank[i]]-1 i+height[rank[i]]1 最大值的位置 M x p [ i ] Mxp[i] Mxp[i] ,当前位置对答案的贡献就为 s u m [ N e x t [ M x p [ i ] ] ] + a [ M x p [ i ] ] ∗ ( N e x t [ M x p [ i ] ] − i − h e i g h t [ r a n k [ i ] ] ) sum[Next[Mxp[i]]]+a[Mxp[i]]*(Next[Mxp[i]]-i-height[rank[i]]) sum[Next[Mxp[i]]]+a[Mxp[i]](Next[Mxp[i]]iheight[rank[i]])

最后就是多组数据的处理,若将 N e x t [ i ] Next[i] Next[i] 不存在时赋值为 n + 1 n+1 n+1 ,需要将 a [ n + 1 ] a[n+1] a[n+1] 清零!!!死在这里死了好久枯了。

【代码】

#include <bits/stdc++.h>
#define N 200005
using namespace std;
typedef long long ll;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-' ) f=-1;ch=getchar();}
	while(isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}

int T,n,tot,m;
int sa[N],height[N],Rank[N],Hash[N],x[N],y[N],a[N],c[N],Next[N],Mxp[N];
ll sum[N];
ll Ans;

class Seg_Tree{
	public:
		int l,r,mxp;
}e[N<<2];

void Build(int p,int l,int r)
{
	e[p].l=l,e[p].r=r;
	if(l==r) {
		e[p].mxp=l;
		return;
	}
	int mid=l+r>>1;
	Build(p<<1,l,mid);Build(p<<1|1,mid+1,r);
	e[p].mxp=a[e[p<<1].mxp]>=a[e[p<<1|1].mxp]?e[p<<1].mxp:e[p<<1|1].mxp;
}

int Find_Max(int p,int x,int y)
{
	int l=e[p].l,r=e[p].r;
	if(l==x&&r==y) return e[p].mxp;
	int mid=l+r>>1;
	if(y<=mid) return Find_Max(p<<1,x,y);
	if(x>mid) return Find_Max(p<<1|1,x,y);
	int lm=Find_Max(p<<1,x,mid),rm=Find_Max(p<<1|1,mid+1,y);
	return a[lm]>=a[rm]?lm:rm;
}

int Find(int x)
{
	int l=1,r=tot,rtn=0;
	while(l<=r)
	{
		int mid=l+r>>1;
		if(Hash[mid]>=x) rtn=mid,r=mid-1;
		else l=mid+1;
	}
	return rtn;
}

void Init()
{
	n=read();Ans=0;a[n+1]=0;
	for(int i=1;i<=n;i++) Hash[i]=a[i]=read();
	sort(Hash+1,Hash+1+n);
	tot=unique(Hash+1,Hash+1+n)-Hash-1;
	for(int i=1;i<=n;i++) a[i]=Find(a[i]);
}

void Build_Sa()
{
	m=tot+1;
	for(int i=1;i<=m;i++) c[i]=0;
	for(int i=1;i<=n;i++) c[x[i]=a[i]]++;
	for(int i=1;i<=m;i++) c[i]+=c[i-1];
	for(int i=n;i;i--) sa[c[x[i]]--]=i;
	for(int k=1;k<=n;k<<=1) 
	{
		int p=0;
		for(int i=n-k+1;i<=n;i++) y[++p]=i;
		for(int i=1;i<=n;i++) if(sa[i]>k) y[++p]=sa[i]-k;
		for(int i=1;i<=m;i++) c[i]=0;
		for(int i=1;i<=n;i++) c[x[y[i]]]++;
		for(int i=1;i<=m;i++) c[i]+=c[i-1];
		for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i];
		swap(x,y);x[sa[1]]=1;p=1;
		for(int i=2;i<=n;i++) 
			x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p:++p;
		if(p>=n) break; m=p;
	}
}

void Get_Height()
{
	for(int i=1;i<=n;i++) Rank[sa[i]]=i;
	int k=0;
	for(int i=1;i<=n;i++)
	{
		if(k) k--;
		int j=sa[Rank[i]-1];
		while(a[i+k]==a[j+k]) k++;
		height[Rank[i]]=k; 
	}
	for(int i=1;i<=n;i++) 
		Mxp[i]=height[Rank[i]]==0?0:Find_Max(1,i,i+height[Rank[i]]-1);
}

stack<int>st;
void Get_Next()
{
	while(!st.empty()) st.pop();
	for(int i=1;i<=n;i++) Next[i]=n+1;
	for(int i=1;i<=n;i++)
	{
		while(!st.empty()&&a[st.top()]<a[i]) 
		{
			Next[st.top()]=i;
			st.pop();
		}	
		st.push(i);
	}
	sum[n+1]=0;
	for(int i=1;i<=n;i++) sum[i]=1LL*Hash[a[i]]*(Next[i]-i);
	for(int i=n;i;i--) sum[i]+=sum[Next[i]];
}

void Solve()
{
	for(int i=1;i<=n;i++)
	{
		if(!Mxp[i]) Ans+=sum[i];
		else
			Ans+=sum[Next[Mxp[i]]]+1LL*Hash[a[Mxp[i]]]*(Next[Mxp[i]]-i-height[Rank[i]]);
	}
	printf("%lld\n",Ans);
}

int main()
{
	T=read();
	while(T--)
	{
		Init();
		Build_Sa();
		Build(1,1,n);
		Get_Height();
		Get_Next();
		Solve();
 	}
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值