Train Hard, Win Easy
首先这道题最大的难点就是理解题意,各种翻译软件翻译出来的几乎完全看不懂,只能慢慢的借助样例解释慢慢的来了解题意。
题意大概如下:
总共有两道题,给你n个人做每道题目的时间,之后是m个关系,这m对人无法组队,两道题目两个人组队一人一道,每次组队的时候要使总时间最少,求每个人和其他能组队的人组队的得分和
数据范围:2<=n<=3e5,0<=m<=3e5
首先考虑暴力是否可行
要求没两个人的两两组合,n^2,显然不行
再开始想正解
依据题意
我们先假设m为0
那么
a n s i = ∑ i n m i n ( a i + b j , b i + a j ) ansi= \sum_{i}^{n} min(ai+bj,bi+aj) ansi=i∑nmin(ai+bj,bi+aj)
也就是说
当ai+bj<bi+aj时对答案的贡献是ai+bj
反之则是bi+aj
我们将其移下项
bj-aj<bi-ai时对答案的贡献是ai+bj
现在已经看到正解的尾巴了
如果我们将每个人的bi-ai进行从小到大排序,在这个人前面的人(记为j)的bj-aj都会小于bi-ai
那么对答案的贡献就是ai+bj,也就是前面所有人的bj都和ai进行配对,然后我们只要借助前缀和进行优化
就可以求出前i-1个人(排好序的数组里的编号)和i组队的最小答题时间和,即为sumb+ai*(i-1)
后面的同理,应该为suma+ai*(n-i)
这里的suma是从n到1的a[i]的前缀和
sumb是1到n的b[i]的前缀和
然后最后减去不能组队的情况,就解决了
代码如下(时间复杂度nlogn):
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=3000005;
int n,m;
ll a[maxn],b[maxn];
struct num_node{
ll num;
int id;
}s[maxn];
ll ans[maxn];
bool cmp(num_node a,num_node b){
return a.num<b.num;
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i){
ll x,y;
scanf("%lld %lld",&x,&y);
a[i]=x;b[i]=y;
s[i].num=y-x;
s[i].id=i;
}
sort(s+1,s+n+1,cmp);
ll suma=0,sumb=0;//这里必须开long long
for(int i=1;i<=n;++i){
ans[s[i].id]+=a[s[i].id]*(i-1)+sumb;
sumb+=b[s[i].id];
}
for(int i=n;i>=1;--i){
ans[s[i].id]+=b[s[i].id]*(n-i)+suma;
suma+=a[s[i].id];
}
while(m--){
ll x,y;
scanf("%lld %lld",&x,&y);
ans[x]-=min(b[x]+a[y],b[y]+a[x]);
ans[y]-=min(b[x]+a[y],b[y]+a[x]);
}
for(int i=1;i<=n;++i){
printf("%lld",ans[i]);
if(i!=n)printf(" ");
}
return 0;
}