题意
每个人都可以解决A题和B题,得分分别是 x i x_i xi和 y i y_i yi,他可以任意和他不讨厌的人组队,一个队伍中每个人都只能做 A A A和 B B B中的一道题,且每道题都只能由一个人来完成,队伍中两道题得分的总和越少越好。求出每个人所参与的所有队伍的得分总和。
题解
现在有c和d两个人组队,他们对于A题和B题的得分分别是, x c , y c x_c,y_c xc,yc和 x d , y d x_d,y_d xd,yd。c可以做A题的条件是 x c + y d ≤ x d + y c x_c+y_d \leq x_d+y_c xc+yd≤xd+yc。移项一下变成 y d − x d ≤ y c − x c y_d-x_d \leq y_c-x_c yd−xd≤yc−xc。 所以我们可以按照每个人 y − x y-x y−x进行排序,假设现在计算第i个人的得分,那么如果他和前 i − 1 i-1 i−1个人组队的话,他一直做的都是A题和后 n − i + 1 n-i+1 n−i+1个人组队的话做的都是B题。再分别求 x x x和 y y y的前缀和就可以做到 O ( 1 ) O(1) O(1)计算一个人的得分。最后再根据讨厌的关系减一减就可以了。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5+5;
struct node {
ll x,y;
int id;
ll val;
bool operator <(const node& rhs) const {
return val < rhs.val;
}
}a[maxn], b[maxn];
ll prex[maxn], prey[maxn];
ll ans[maxn];
int n,m;
ll get(ll x, ll _x, ll _y, int pos) {
ll px = pos;
// cout << px <<" ";
ll ret = 0;
ret += prey[px]+_x*px;
// cout << ret <<" ";
ret += prex[n]-prex[px]+_y*(n-px);
// cout << ret <<" ";
ret -= (_x+_y);
// cout << ret <<endl;
return ret;
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) {
scanf("%lld%lld", &a[i].x, &a[i].y);
a[i].val = a[i].y-a[i].x;
b[i].x = a[i].x; b[i].y = a[i].y;
a[i].id = i;
}
sort(a+1,a+n+1);
// cout << a[4].val << endl;
for(int i = 1; i <= n; ++i) {
prex[i] = prex[i-1]+a[i].x;
prey[i] = prey[i-1]+a[i].y;
}
for(int i = 1; i <= n; ++i) {
// cout <<i <<" ";
ans[a[i].id] = get(a[i].y-a[i].x, a[i].x, a[i].y, i);
// cout << endl;
}
for(int i = 0; i < m; ++i) {
ll u,v;
scanf("%lld%lld", &u, &v);
ll ret = min(b[u].x+b[v].y, b[u].y+b[v].x);
// cout << ret << endl;
ans[u] -= ret;
ans[v] -= ret;
}
for(int i = 1; i <= n; ++i)
cout << ans[i] <<" ";
cout << endl;
return 0;
}