P3971 [TJOI2014] Alice and Bob
P3971 [TJOI2014] Alice and Bob
挺奇妙一贪心,思路很流畅,一步步来分析。
首先发现序列
然后因为
这里贪心的想就是越往后的数越小越好,这里严谨证明不是很会,感性理解吧,然后找一找性质。
-
性质一:
a 相同的数,肯定是单调递减的。 -
性质二:对于每个数
x_i ,他前面必有一个j 满足a_j = a_i - 1 且x_j < x_i 。 -
性质三:对于任意一个
j 满足j < i 且a_j \ge a_i ,必须满足x_j > x_i 。
由性质一和性质二可得
至此我们得到了一颗树,如果你按加边顺序倒序遍历一遍后,会发现得到了一个序列
至于证明,我看其他题解没看懂所以才来写的这篇,也不算严谨证明。
首先遍历这颗树得到的序列是指这个,为了方便我就这么说了。
void dfs(int u)
{
x[u] = cnt ++ ;
for (int i = h[u]; ~i; i = ne[i])
dfs(e[i]);
}
首先这颗树画成分层图,其每一层的
然后可以发现我们得到的序列必然满足性质二,而倒序遍历边会满足性质一和性质三,也就是相同的数先遍历后面的,这些画图都能看出来。
那它是否满足贪心呢?画图也能看出来。
因为我们建树是找前面满足条件的最靠后的一个数,所以每一层的数是把他们分了几块,互不相交。
比如第一层的
所以我们倒序遍历边可以时使得越往后的数越小,且同时满足三个性质或者说限制。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010;
int n;
int h[N], e[N], ne[N], idx;
int w[N], p[N], cnt;
int last[N], tr[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
int lowbit(int x)
{
return x & -x;
}
int query(int x)
{
int res = 0;
for (int i = x; i; i -= lowbit(i)) res = max(res, tr[i]);
return res;
}
void update(int x, int v)
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] = max(tr[i], v);
}
void dfs(int u)
{
p[u] = cnt ++ ;
for (int i = h[u]; ~i; i = ne[i])
dfs(e[i]);
}
int main()
{
scanf("%d", &n);
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &w[i]), last[w[i]] = i;
add(last[w[i] - 1], i);
}
dfs(0);
LL res = 0;
for (int i = n; i; i -- )
{
int t = query(p[i]) + 1;
update(p[i], t), res += t;
}
cout << res << endl;
return 0;
}