【2019/01/20测试T3】Alice&Bob

【题目】

传送门

题目描述:

AliceBob 发明了一个新的游戏。给定一个序列 { x 0 , x 1 , ⋅ ⋅ ⋅ , x n − 1 } \{x_0,x_1,⋅⋅⋅,x_{n−1}\} {x0,x1,,xn1}

Alice 得到一个序列 { a 0 , a 1 , ⋅ ⋅ ⋅ , a n − 1 } \{a_0,a_1,⋅⋅⋅,a_{n−1}\} {a0,a1,,an1},其中 a i a_i ai 表示以 x i x_i xi 结尾的最长上升子序列的长度;

Bob 得到一个序列 { b 0 , b 1 , ⋅ ⋅ ⋅ , b n − 1 } \{b_0,b_1,⋅⋅⋅,b_{n−1}\} {b0,b1,,bn1},其中 b i b_i bi 表示以 x i x_i xi 开头的最长下降子序列的长度。

Alice 的得分是序列 { a 0 , a 1 , ⋅ ⋅ ⋅ , a n − 1 } \{a_0,a_1,⋅⋅⋅,a_{n−1}\} {a0,a1,,an1} 的和,Bob 的得分是 { b 0 , b 1 , , ⋅ ⋅ ⋅ , b n − 1 } \{b_0,b_1,,⋅⋅⋅,b_{n−1}\} {b0,b1,,,bn1} 的和。

输入格式:

输入的第一行是 n n n,第二行是序列 { a 0 , a 1 , ⋅ ⋅ ⋅ , a n − 1 } \{a_0,a_1,⋅⋅⋅,a_{n−1}\} {a0,a1,,an1}

数据保证序列 a a a 可以由至少一个 1 1 1 n n n 的排列得到。

输出格式:

输出包含一行,表示 Bob 能得到的最高分数。

样例数据:

输入
4
1 1 2 3

输出
5

提示:

对于 30 % 30\% 30% 的数据, n ≤ 1000 n ≤ 1000 n1000

对于 100 % 100\% 100% 的数据, n ≤ 1 0 5 n≤10^5 n105


【分析】

首先,易知在满足题意的条件下,如果在前面的数越大,结果肯定更优

有两个比较容易得出的结论:

  1. i &lt; j i&lt;j i<j a i = a j a_i=a_j ai=aj 那么可知 x i &gt; x j x_i&gt;x_j xi>xj
  2. 对于 a i ( a i ≠ 1 ) a_i(a_i\ne 1) ai(ai̸=1),肯定存在 j ( j &lt; i ) j(j&lt;i) j(j<i),使得 a i = a j + 1 a_i=a_j+1 ai=aj+1

这两个结论应该是很容易理解的吧,就不多做解释了

然后按照这两个建边,跑拓扑排序,确定点之间的大小关系

对于结论 2 2 2,如果有多个点满足题意,为了让前面的数更大,取最近的那个就可以了

然后根据大小关系分配权值,翻转一下跑个最长上升子序列就可以了


【代码】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define M 2000005
using namespace std;
int n,t;
int first[N],v[M],nxt[M];
int a[N],d[N],f[N],X[N],in[N],pre[N];
priority_queue<int,vector<int>,greater<int> >q;
void add(int x,int y)
{
	t++;
	nxt[t]=first[x];
	first[x]=t;
	v[t]=y;
}
void Topology()
{
	int x,i,tot=n;
	for(i=1;i<=n;++i)
	  if(!in[i])
	    q.push(i);
	while(!q.empty())
	{
		x=q.top();q.pop();X[x]=tot--;
		for(i=first[x];i;i=nxt[i])
		{
			in[v[i]]--;
			if(!in[v[i]])  q.push(v[i]);
		}
	}
}
int main()
{
//	freopen("alice.in","r",stdin);
//	freopen("alice.out","w",stdout);
	int x,i;
	scanf("%d",&n);
	memset(pre,-1,sizeof(pre));
	for(i=1;i<=n;++i)
	{
		scanf("%d",&x);
		if(~pre[x])  add(pre[x],i),in[i]++;
		if(~pre[x-1])  add(i,pre[x-1]),in[pre[x-1]]++;
		pre[x]=i;
	}
	Topology();
	reverse(X+1,X+n+1);
	int k=0;
	long long ans=0;
	for(i=1;i<=n;++i)
	{
		if(X[i]>d[k])  d[++k]=X[i],f[i]=k;
		else  d[f[i]=lower_bound(d+1,d+k+1,X[i])-d]=X[i];
		ans+=f[i];
	}
	printf("%lld",ans);
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值