传送门
题目描述:
Alice 和 Bob 发明了一个新的游戏。给定一个序列 { x 0 , x 1 , ⋅ ⋅ ⋅ , x n − 1 } \{x_0,x_1,⋅⋅⋅,x_{n−1}\} {x0,x1,⋅⋅⋅,xn−1}。
Alice 得到一个序列 { a 0 , a 1 , ⋅ ⋅ ⋅ , a n − 1 } \{a_0,a_1,⋅⋅⋅,a_{n−1}\} {a0,a1,⋅⋅⋅,an−1},其中 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,⋅⋅⋅,bn−1},其中 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,⋅⋅⋅,an−1} 的和,Bob 的得分是 { b 0 , b 1 , , ⋅ ⋅ ⋅ , b n − 1 } \{b_0,b_1,,⋅⋅⋅,b_{n−1}\} {b0,b1,,⋅⋅⋅,bn−1} 的和。
输入格式:
输入的第一行是 n n n,第二行是序列 { a 0 , a 1 , ⋅ ⋅ ⋅ , a n − 1 } \{a_0,a_1,⋅⋅⋅,a_{n−1}\} {a0,a1,⋅⋅⋅,an−1}。
数据保证序列 a a a 可以由至少一个 1 1 1 到 n n n 的排列得到。
输出格式:
输出包含一行,表示 Bob 能得到的最高分数。
样例数据:
输入
4
1 1 2 3
输出
5
提示:
对于 30 % 30\% 30% 的数据, n ≤ 1000 n ≤ 1000 n≤1000
对于 100 % 100\% 100% 的数据, n ≤ 1 0 5 n≤10^5 n≤105
首先,易知在满足题意的条件下,如果在前面的数越大,结果肯定更优
有两个比较容易得出的结论:
这两个结论应该是很容易理解的吧,就不多做解释了
然后按照这两个建边,跑拓扑排序,确定点之间的大小关系
对于结论 2 2 2,如果有多个点满足题意,为了让前面的数更大,取最近的那个就可以了
然后根据大小关系分配权值,翻转一下跑个最长上升子序列就可以了
#include
#include
#include
#include
#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;
}