【后缀数组】【线段树】codeforces102028H Can You Solve the Harder Problem?

H. Can You Solve the Harder Problem?
time limit per test6.0 s
memory limit per test1024 MB
inputstandard input
outputstandard output
You are given a sequence a denoted by (a1,a2,⋯,an). A query with 1≤l≤r≤n is defined as
Qmax(l,r)=max{al,al+1,⋯,ar}.
An easy problem may ask you to calculate the sum of answers for all queries with integers 1≤l≤r≤n, but we would like to show you a harder one.

Define a classifier as a container that stores unique elements. Each element in the classifier has two values, named the key value and the mapped value. The key value of each element is a consecutive subsequence of a and the mapped value of that element is an integer indicating the maximum value in the subsequence. The classifier only stores elements with distinct key values, which means extra duplicated elements (with the same key value) would be removed from the classifier.

Denote S(l,r) as (al,al+1,⋯,ar), a consecutive subsequence of a meeting the condition that l and r are integers with 1≤l≤r≤n. Now we intend to use a classifier CA to store all the consecutive subsequences S(l,r) of a with their Qmax(l,r). You are asked to determine the sum of mapped values in CA.

Actually, what we defined above is a map of the form map<vector, int> in C++ or Map<ArrayList, Integer> in Java, so if you are seasoned using these data structures, you may realize that what we intend to do is to insert all possible elements (S(l,r),Qmax(l,r)) with integers 1≤l≤r≤n into the classifier CA and then ask you to calculate the sum of mapped values in it.

Input
The input contains several test cases, and the first line contains a positive integer T indicating the number of test cases which is up to 1000.

For each test case, the first line contains an integer n indicating the number length of the given sequence a, where 1≤n≤2×105.

The second line contains n positive integers a1,a2,⋯,an describing the sequence a, where 1≤ai≤106.

We guarantee that there are at most 10 test cases with n>1000.

Output
For each test case, output a line containing the sum of mapped values in CA.

Example
inputCopy
2
3
1 2 3
3
2 3 3
outputCopy
14
14
Note
In the first sample case, the sum of mapped values in CA is equal to 1+2+3+2+3+3.

In the second sample case, the sum of mapped values in CA is equal to 2+3+3+3+3.


 虽然最近很忙,但是没更博客简直是让人于心有愧…快到考期了,忙的东西少了,需要加紧搞ACM了。希望尽快达到黄名水平。
 这题想到后缀数组简直是天经地义。然而之后我的思路就被钳制住了,不知道怎么利用height数组高效去重。后来看了题解才逐渐豁然开朗。才知道自己学了一个死的算法,不懂如何灵活运用。后缀数组和线段树应该是一个普遍的组合了,自己以后还是要多接触难题好。
 首先,对于原先的simpler problem,我们有很多搞法。其中一种看似繁琐,但是可以应用到之后的算法中:从右往左逐个添加元素a[i]到线段树,线段树中每个元素是i到j之间的a的最大值,维护连续和。每次添加的时候,实际上都是对线段树一段区间做一个区间set。这是因为显然线段树中每个元素递增,所以添加一个元素更新最大值的时候,左侧每个的部分会被更改成新值,而右边的部分不变。至于这个区间的右上限,可以用单调栈预处理出每个右侧第一个不小于a[i]的位置。最后,每次把线段树总和累加,就是最终答案了。
 不过harder problem,因为要去重,所以线段树是必要的。去重实际上是对sa的height数组对应的重复串的所有子串求和,然后去掉这个和。height一共n-1个,所以做n-1次区间query就行了。


 如果不用单调栈预处理,直接线段树求上限也可以,不过需要多维护一维区间中值。还是单调栈好一些。
 这里的后缀数组因为字符集比较大,有1E6,所以可以先离散化,让它不超过n,加快速度。

 最后佩服一下赛场上打出这题的同学们。

#include<cstdio>
#include<algorithm>
#define fx first
#define sy second
#define kl (k<<1)
#define kr (k<<1|1)
#define M (L+R>>1)
#define lin L,M
#define rin M+1,R
using namespace std;
using LL=long long;

int s[200005],sa[200005],t[200005],t2[200005],c[200005],n,Tt;
int rk[200005],ht[200005],dec[200005];
LL T[1<<19],ans;
int setv[1<<19];
int Rp[200005],top;
pair<int,int> S[200005],a[200005];

void build_sa(int n)
{
	int i,*x=t,*y=t2,m=n,p=0;
	for(i=0;i<m;i++)
		c[i]=0;
	for(i=0;i<n;i++)
		c[x[i]=s[i]]++; 
	for(i=1;i<m;i++)
		c[i]+=c[i-1];
	for(i=n-1;i>=0;i--)
		sa[--c[x[i]]]=i;
	for(int k=1;k<=n&&p<n;k<<=1)
	{
		p=0;
		for(i=n-k;i<n;i++)
			y[p++]=i;
		for(i=0;i<n;i++)
			if(sa[i]>=k)
				y[p++]=sa[i]-k;
		for(i=0;i<m;i++)
			c[i]=0;
		for(i=0;i<n;i++)
			c[x[y[i]]]++;
		for(i=1;i<m;i++)
			c[i]+=c[i-1];
		for(i=n-1;i>=0;i--)
			sa[--c[x[y[i]]]]=y[i];
		swap(x,y);
		p=1;
		x[sa[0]]=0;
		for(i=1;i<n;i++)
			x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&
			sa[i-1]+k<n&&sa[i]+k<n&&y[sa[i-1]+k]==y[sa[i]+k]?
			p-1:p++;
		m=p;
	}
}

void getHeight()
{
	int i,j,k=0;
	for(i=0;i<n;i++)
		rk[sa[i]]=i;
	for(i=0;i<n;i++)
	{
		if(!rk[i])
			continue;
		if(k)
			--k;
		j=sa[rk[i]-1];
		while(j+k<n&&i+k<n&&s[i+k]==s[j+k])
			++k;
		ht[rk[i]]=k;
	}
}

void getRightLimit(int n)
{
	for(int i=0;i<n;i++)
	{
		while(top>0&&S[top-1].fx<=s[i])
			Rp[S[--top].sy]=i;
		S[top++]={s[i],i};
	}
	while(top>0)
		Rp[S[--top].sy]=n;
}

void build_tree(int k, int L, int R)
{
	if(L==R)
	{
		T[k]=setv[k]=0;
		return ;
	}
	build_tree(kl,lin);
	build_tree(kr,rin);
	T[k]=setv[k]=0;
}

void push_down(int k, int L, int R)
{
	if(setv[k])
	{
		setv[kl]=setv[k];
		setv[kr]=setv[k];
		T[k]=(LL)setv[k]*(R-L+1);
		setv[k]=0;
	}
}

void set(int k, int L, int R, int l, int r, int val)
{
	if(L>=l&&R<=r)
	{
		setv[k]=val;
		return ;
	}
	push_down(k,L,R);
	if(l<=M)
		set(kl,lin,l,r,val);
	if(r>M)
		set(kr,rin,l,r,val);
	T[k]=(setv[kl]?(LL)setv[kl]*(M-L+1):T[kl])+(setv[kr]?(LL)setv[kr]*(R-M):T[kr]);
}

LL query(int k, int L, int R, int l, int r)
{
	if(l>r)
		return 0;
	if(l<=L&&R<=r)
		return setv[k]?(LL)setv[k]*(R-L+1):T[k];
	LL res=0;
	push_down(k,L,R);
	if(l<=M)
		res+=query(kl,lin,l,r);
	if(r>M)
		res+=query(kr,rin,l,r);
	return res;
}

int main()
{
	scanf("%d",&Tt);
	while(Tt--)
	{
		scanf("%d",&n);
		for(int i=0;i<n;i++)
			scanf("%d",&a[i].fx),a[i].sy=i,dec[i]=0;
		sort(a,a+n);
		for(int i=0,j=0;i<n;i++)
		{
			s[a[i].sy]=a[i].fx==a[i-1].fx&&j?j-1:j++;
			a[i].sy=0;
		}
		unique(a,a+n);
		build_sa(n);
		getHeight();
		ans=0;
		build_tree(1,0,n-1);
		getRightLimit(n);
		for(int i=1;i<n;i++)
			dec[sa[i]]=ht[i];
		for(int i=n-1;i>=0;i--)
		{
			set(1,0,n-1,i,Rp[i]-1,a[s[i]].fx);
			ans+=setv[1]?(LL)setv[1]*(n-i):T[1]; 
			ans-=query(1,0,n-1,i,dec[i]+i-1);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/BUAA_Alchemist/article/details/85054995