逃离亚马逊
【题目背景】
“everlasting你已经死了,你能做的只有„„”
【问题描述】
everlasting带着 N个蒟蒻要从亚马逊丛林当中逃出来,这是一场艰苦的战斗,只有众人
团结一心才能披荆斩棘,勇往直前。蒟蒻就是蒟蒻,这 N 个蒟蒻互相不服,每个蒟蒻有三
个属性:实力值,信心值和财富值,其中第 i个蒟蒻的实力值 ai(ai<=i)、财富值ci是已知的,
而信心值需要经过一系列战斗来决出。
战斗的方式是这样的,所有的蒟蒻按照编号从大到小的顺序依次入场。第 i个蒟蒻的信
心值一开始等于他的实力值,然后他会依次面对 i+1 号~N 号蒟蒻,如果他当前面对的这个
人的实力值小于等于他自己当前的信心值,他就会说一句“你太弱了”,然后自我膨胀(信心
值++)。每个蒟蒻在通过比较确定了自己的信心值之后,他的信心值就确定了,然后等待下
一个蒟蒻的入场。
everlasting想要组建一支人数最多的队伍,这支队伍必须不存在任何一对蒟蒻互相不服。
一对蒟蒻如果互相不服当且仅当其中一个人的信心值小于对方且财富值大于对方,
everlasting想知道这支队伍最多能有多少人?
【输入格式】
第一行一个正整数N 代表蒟蒻人数
接下来一行N 个正整数 ai代表每个蒟蒻的实力值,其中 ai<=i
接下来一行N 个正整数 ci代表每个蒟蒻的财富值,保证 ci互不相同
【输出格式】
一个数表示队伍中最多有多少人
【样例输入】
3
1 1 2
2 1 3
【样例输出】
2
【数据规模与约定】
对于30%的数据,N<=3000
对于另外20%的数据,ai=i
对于100%的数据,N<=100000,ci<=N
首先,有一个结论就是最后的信心值是一个排列。
考虑证明这个东西。
显然最后的信心值一定是[1,n]中的整数,因为$a_i<i$
考虑从某个元素$i$开始一个个的挑战,比如到第$j$个元素的时候。
如果当前的值小于$a_j$,那么信心值一定小于$j$的信心值。
如果当前的值大于等于$a_j$,那么信心值一定大于$j$的信心值。
所以所有人的信心值两两不同。
所以最后的信心值是一个排列。
我们维护一个序列,序列的初始值就是1到n
每次就是取走第$a_i$小的元素作为$i$的信心值。
我们思考一下这个东西的正确性。
下文中的跳跃指的是到下一个没有被取走的元素。
考虑有两个位置$i$和$j$,$i<j$
$j$的信心值已经被算过了为$B+a_j$。
我们考虑如果让$i$向后跳,如果他能够跳到$a_j+B$的位置就说明可以在$j$的这个位置进行信心值增加操作。
我们在计算$j$的时候有$B$次跳跃,我们从$a_i$开始跳跃,跳跃到$a_j+B$时
有$B$个跳跃中跨过的元素对应的位置不在$[i,j]$区间中。
所以该统计进去的位置$j$都会被统计到。
大概就是意识流一下。。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define M 100010 4 inline int read() { 5 char ch = getchar(); int x = 0, f = 1; 6 while(ch < '0' || ch > '9') { 7 if(ch == '-') f = -1; 8 ch = getchar(); 9 } 10 while('0' <= ch && ch <= '9') { 11 x = x * 10 + ch - '0'; 12 ch = getchar(); 13 } 14 return x * f; 15 } 16 int a[M]; 17 struct Node{ 18 int A, B; 19 inline bool operator < (const Node& rhs) const { 20 return A < rhs.A; 21 } 22 } b[M]; 23 int q[M * 4]; 24 inline void build(int l, int r, int o) { 25 q[o] = r - l + 1; 26 if(l == r) return; 27 int mid = (l + r) / 2; 28 build(l, mid, 2 * o); 29 build(mid + 1, r, 2 * o + 1); 30 } 31 inline int change(int l, int r, int o, int x) { 32 -- q[o]; 33 if(l == r) return l; 34 int mid = (l + r) / 2; 35 if(q[2 * o] >= x) return change(l, mid, 2 * o, x); 36 return change(mid + 1, r, 2 * o + 1, x - q[2 * o]); 37 } 38 int C[M]; 39 int n; 40 inline void add(int x, int y) { 41 for(int i = x; i <= n; i += (i & -i)) { 42 C[i] = max(C[i], y); 43 } 44 } 45 inline int query(int x) { 46 int ret = 0; 47 for(int i = x; i; i -= (i & -i)) { 48 ret = max(ret, C[i]); 49 } 50 return ret; 51 } 52 int main() { 53 n = read(); 54 for(int i = 1; i <= n; ++ i) { 55 a[i] = read(); 56 } 57 for(int i = 1; i <= n; ++ i) { 58 b[i].A = read(); 59 } 60 build(1, n, 1); 61 for(int i = n; i >= 1; -- i) { 62 b[i].B = change(1, n, 1, a[i]); 63 } 64 sort(b + 1, b + n + 1); 65 int Ans = 1; 66 for(int i = 1; i <= n; ++ i) { 67 int wt = query(b[i].B) + 1; 68 Ans = max(Ans, wt); 69 add(b[i].B, wt); 70 } 71 printf("%d\n", Ans); 72 }