题目
分析
为了方便叙述,记精灵为 X X X,矮人为 Y Y Y, X X X要尽可能多地打败 Y Y Y。
还是要先证明一个结论:我们可以找到一个 Y j Y_j Yj,使得所有的 X X X,无论怎么安排入场顺序,寻找对手时都不会越过它。
定义 A i A_i Ai表示对手为 Y i Y_i Yi的 X X X的人数, B i B_i Bi为 A i A_i Ai的前缀和,如果有一段区间 [ i , j ] [i,j] [i,j]满足 B j − B i − 1 ≤ j − i + 1 B_j-B_{i-1}\leq j-i+1 Bj−Bi−1≤j−i+1,那么可以肯定 [ i , j ] [i,j] [i,j]这一段内,无论怎么安排 X X X的入场顺序,它们都不可能在找对手时越过 B j B_j Bj,因为就算每个 B p ( p ∈ [ i , j ] ) B_p(p\in [i,j]) Bp(p∈[i,j])都找到了对手,也达不到 j − i + 1 j-i+1 j−i+1个人。
这点一定要想通。
接下来:由于 B j − B i − 1 ≤ j − i + 1 B_j-B_{i-1}\leq j-i+1 Bj−Bi−1≤j−i+1,所以 B j − j ≤ B i − 1 − ( i − 1 ) B_j-j\leq B_{i-1}-(i-1) Bj−j≤Bi−1−(i−1)
于是,再定义
C
i
=
B
i
−
i
C_i=B_i-i
Ci=Bi−i,发现如果
[
i
,
j
]
[i,j]
[i,j]这一段内无法越过
B
j
B_j
Bj,一定有
C
j
≤
C
i
C_j\leq C_i
Cj≤Ci。
我们要找的是对于所有的
[
i
,
j
]
(
i
∈
[
1
,
n
]
)
[i,j](i\in [1,n])
[i,j](i∈[1,n]),都满足
C
j
≤
C
i
C_j\leq C_i
Cj≤Ci的那个
j
j
j。
所以,有 min C j \min C_j minCj所对应的那个 j j j满足题意。
这个结论有什么用?
找到了这个点 j j j过后,就可以从这个点断开, Y 1 , . . . , Y n Y_1,...,Y_n Y1,...,Yn的环就变成了 Y j + 1 , . . . , Y n , Y 1 , . . . , Y j Y_j+1,...,Y_n,Y_1,...,Y_j Yj+1,...,Yn,Y1,...,Yj的链。
那么就可以贪心啦:
从
Y
i
Y_i
Yi原本的对手中给它选一个可以打败它的实力最小的
X
X
X(不行就跳过,无法打败),删去
X
X
X,把剩下的对手传给
Y
i
+
1
Y_{i+1}
Yi+1(这个“传”的操作保证了贪心的正确性,因为更大实力的传下去可能碰到更适合的,更小的你如果不传后面可能就没用了),重复此操作即可。
代码
贪心用set
维护。
#include<set>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define MAXN 500000
#define INF 0x7fffffff
int N;
struct Spirit{
int C,V;
}A[MAXN+5];
struct Dwaft{
int P;
vector<int> D;
//这个vector存每个对手
}B[MAXN+5];
set<int> Attack;
int main(){
freopen("kralj.in" ,"r", stdin);
freopen("kralj.out","w",stdout);
scanf("%d",&N);
for(int i=1;i<=N;i++){
scanf("%d",&A[i].C);
B[A[i].C].D.push_back(i);
}
int C=0,Min=INF,cut=0;
for(int i=1;i<=N;i++){
C+=B[i].D.size()-1;
//C就是之前提到的数组C的最后一位,递推式化一下很简单就不用数组了
if(C<Min)
Min=C,cut=i;
}
for(int i=1;i<=N;i++)
scanf("%d",&B[i].P);
for(int i=1;i<=N;i++)
scanf("%d",&A[i].V);
int Ans=0;
for(int i=cut%N+1,cnt=1;cnt<=N;i=i%N+1,cnt++){
for(int j=0;j<int(B[i].D.size());j++)
Attack.insert(A[B[i].D[j]].V);//把新对手放进去
set<int>::iterator it=Attack.lower_bound(B[i].P);//找能攻击的一个
if(it!=Attack.end()){//找到
Ans++;
Attack.erase(it);
}
else//没找到
Attack.erase(Attack.begin());
}
printf("%d",Ans);
}