CF1043E Solution
题解
可以发现枚举两个在一组里的数会TLE,加之只有两个参数限制与数据范围,容易想到排序算法。
计算第\(i\)个人的分数和需要知道他在每一组里是\(x\)还是\(y\),因此排序需要满足\(a_i\)前的数与\(a_i\)组合时为\(x\)更优,而\(a_i\)后的数与\(a_i\)组合时为\(y\)更优(反之亦可),也就是\(x_i+y_{i+1}<x_{i+1}+y_i\)。
而对于\(a_i\),则分数和:
\[=\sum\limits_{j=1}^{i-1} (x_j+y_i)+\sum\limits_{j=i+1}^n(x_i+y_j)\\
=\sum\limits_{j=1}^{i-1} x_j+y_i\cdot (i-1)+\sum\limits_{j=i+1}^ny_j+x_i\cdot (i-1)
\]
两个\(\Sigma\)用前缀和维护即可。
此外,对于不可以组队的两个数,记录它们组队时的分数,在统计答案时分别减去即可。
AC代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+10;
struct node{int x,y,id;} a[N];
bool cmp(node a,node b) {return a.x+b.y<a.y+b.x;}
int id[N],mi[N],sumx[N],sumy[N];
//id[i]:排序前下标为i的元素排序后的下标,mi[i]:a[i]因不可在一起组队需减去的分数
signed main()
{
int n,m,u,v;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) {scanf("%lld%lld",&a[i].x,&a[i].y); a[i].id=i;}
//排序
sort(a+1,a+n+1,cmp);
//处理前缀和与id数组
for(int i=1;i<=n;i++) sumx[i]=sumx[i-1]+a[i].x,sumy[i]=sumy[i-1]+a[i].y;
for(int i=1;i<=n;i++) id[a[i].id]=i;
//处理不可在一起组队的元素
while(m--)
{
scanf("%lld%lld",&u,&v);
if(id[u]<id[v]) mi[u]+=a[id[u]].x+a[id[v]].y,mi[v]+=a[id[u]].x+a[id[v]].y;
else mi[u]+=a[id[u]].y+a[id[v]].x,mi[v]+=a[id[u]].y+a[id[v]].x;
}
//统计答案
for(int i=1;i<=n;i++)
{
int t=id[i];
printf("%lld ",sumx[t-1]+a[t].y*(t-1)+sumy[n]-sumy[t]+a[t].x*(n-t)-mi[i]);
}
return 0;
}