3743. Alice and Bob

题目描述

Sample Input
输入1:

4

1 2 2 3

输入2:

4

1 1 2 3

Sample Output
输出1:

5

输出2:

5

N<=10^5

题解

注意给出的是a

一种神奇的做法

从后往前求max(b[i]),假设b[i+1]~b[n]已经求出,考虑能转移到b[i]的b[j]

设pre[i]表示最后一个a[i]-1的位置(a[0]=0),把(i,a[i])丢到笛卡尔坐标系上,从(i,a[i])向(pre[i],a[pre[i]])连线,则图形成一棵树

若b[j]能转移到b[i],那么i向下做一条不经i点的射线,把射线经过的边断开,则必然能把j点与0点分开

如图,则b[2]可以由b[3]和b[4]转移,但b[3]不能由b[4]转移

证明:

线的实质就是x=a时a数组的转移情况,现要使b最大,那么就要把x尽量上移

如果x[i]经过了一条线,则会导致a[i]改变,不满足要求


每次可以把一个点以及其后面的点的答案向pre传,同时维护以a为关键字的线段树即可

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define fout
using namespace std;

int tr[500002];
int a[100002];
int f[100002];
int f2[100002];
int ls[100002];
int pre[100002];
int n,i,j,k,l;
long long ans;

void change(int t,int l,int r,int x,int s)
{
	int mid=(l+r)/2;
	
	tr[t]=max(tr[t],s);
	if (l==r) return;
	
	if (x<=mid)
	change(t*2,l,mid,x,s);
	else
	change(t*2+1,mid+1,r,x,s);
}

int find(int t,int l,int r,int x,int y)
{
	int mid=(l+r)/2,s,ans=0;
	
	if (x<=l && r<=y)
	return tr[t];
	
	if (x<=mid)
	s=find(t*2,l,mid,x,y),ans=max(ans,s);
	if (mid<y)
	s=find(t*2+1,mid+1,r,x,y),ans=max(ans,s);
	
	return ans;
}

int main()
{
	freopen("alice.in","r",stdin);
	#ifdef fout
	freopen("alice.out","w",stdout);
	#endif
	
	scanf("%d",&n);
	fo(i,1,n)
	scanf("%d",&a[i]);
	
	fo(i,1,n)
	{
		pre[i]=ls[a[i]-1];
		ls[a[i]]=i;
	}
	
	fd(i,n,1)
	{
		f[i]=find(1,0,n,0,a[i]-1)+1;
		
		f2[i]=max(f2[i],f[i]);
		f2[pre[i]]=max(f2[pre[i]],f2[i]);
		
		change(1,0,n,a[i],f2[i]);
		change(1,0,n,a[pre[i]],f2[pre[i]]);
	}
	
	fo(i,1,n)
	ans+=f[i];
	
	printf("%lld
",ans);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}
原文地址:https://www.cnblogs.com/gmh77/p/12134316.html