Codeforces1311F. Moving Points (树状数组 离散化)
https://codeforces.com/problemset/problem/1311/E
题目点对追击问题可以抽象成两个一次函数相交问题,y = v*t+xi,xi为y轴上的截距,v是斜率,那么当且仅当两个函数在第一象限相交时,点对的最小距离是0,如果两个点对不在第一象限相交,则点对最小距离是就是y轴截距绝对值之差。
可以轻易得到在第一象限相交的条件是,假设直线L1和L2,L1的斜率小于L2的斜率,L1的截距小于L2的截距时,两个直线在第一象限没有交点,其余情况均有交点。分别讨论这两种情况即可。
首先离散化,把点的移动速度按从小打到排序一遍,并去重,再开一个结构体存贮点对信息,并按照截距大小排序。
遍历结构体,用两个树状数组维护Vi,和Xi的前缀和,对于P[i]的速度Vi,在speed排序后的数组中查询Vi在speed数组中的位置pos,树状数组查询斜率比P[i]的斜率小的点个数,查询pos位置点的截距之和,那么P[i]对于答案的贡献就是:斜率比P[i]的斜率小的点个数×Xi(截距)- 斜率比P[i]小且截距比P[i]小的点的截距之和,每次把其贡献加入答案之后,在树状数组中去更新pos位置的点个数和和前缀和。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 2e5+5; 5 int n; 6 ll c[maxn],c2[maxn]; 7 int lowbit(int x) { 8 return x & -x; 9 } 10 void add(ll x, ll k) { 11 while (x <= n) { 12 c[x] = c[x] + k; 13 x = x + lowbit(x); 14 } 15 } 16 void add2(ll x, ll k) { 17 while (x <= n) { 18 c2[x] = c2[x] + k; 19 x = x + lowbit(x); 20 } 21 } 22 ll getsum(int x) { 23 ll ans = 0; 24 while (x >= 1) { 25 ans = ans + c[x]; 26 x = x - lowbit(x); 27 } 28 return ans; 29 } 30 ll getsum2(int x){ 31 ll ans = 0; 32 while (x >= 1) { 33 ans = ans + c2[x]; 34 x = x - lowbit(x); 35 } 36 return ans; 37 } 38 struct node{ 39 ll x,v; 40 bool operator < (const node &b) const{ 41 return x<b.x ; 42 } 43 }p[maxn]; 44 map<ll,int> m; 45 vector<int> speed; 46 int main(){ 47 scanf("%d",&n); 48 for(int i = 1;i<=n;i++){ 49 ll x;scanf("%lld",&x); 50 p[i].x = x; 51 } 52 for(int i = 1;i<=n;i++){ 53 ll v;scanf("%lld",&v); 54 p[i].v = v; 55 if(m[v] == 0) m[v] = 1,speed.push_back(v); 56 } 57 ll ans = 0; 58 sort(p+1,p+1+n); 59 sort(speed.begin(),speed.end()); 60 for(int i = 1;i<=n;i++){ 61 ll cur = p[i].v; 62 int pos = upper_bound(speed.begin(),speed.end(),cur) - speed.begin(); 63 ll sum = getsum(pos),cnt = getsum2(pos); 64 ans+=p[i].x*(cnt) - sum; 65 add(pos,p[i].x),add2(pos,1); 66 } 67 cout<<ans; 68 return 0; 69 }