Codeforces Round #519 by Botan Investments - E. Train Hard, Win Easy
题意
有
两
门
考
试
A
,
B
,
n
个
学
生
要
两
两
组
队
参
加
这
场
考
试
有两门考试A,B,n个学生要两两组队参加这场考试
有两门考试A,B,n个学生要两两组队参加这场考试
每
场
考
试
对
两
个
人
的
加
分
均
为
两
个
问
题
的
得
分
总
和
每场考试对两个人的加分均为两个问题的得分总和
每场考试对两个人的加分均为两个问题的得分总和
已
知
每
个
学
生
回
答
A
,
B
问
题
可
得
到
的
分
值
已知每个学生回答A,B问题可得到的分值
已知每个学生回答A,B问题可得到的分值
每
一
次
组
队
考
试
学
生
1
选
择
一
道
题
,
学
生
2
选
择
另
一
道
题
每一次组队考试学生1选择一道题,学生2选择另一道题
每一次组队考试学生1选择一道题,学生2选择另一道题
现
在
想
知
道
每
个
人
的
最
终
最
小
得
分
为
多
少
(
有
m
组
不
可
以
组
队
的
同
学
,
其
他
同
学
两
两
之
间
必
须
组
一
次
队
)
。
现在想知道每个人的最终最小得分为多少(有m组不可以组队的同学,其他同学两两之间必须组一次队)。
现在想知道每个人的最终最小得分为多少(有m组不可以组队的同学,其他同学两两之间必须组一次队)。
n
<
=
3
∗
1
0
5
,
m
<
=
3
∗
1
0
5
n<=3*10^5,m<=3*10^5
n<=3∗105,m<=3∗105
做法
由于这道题n,m的数据范围都很大,我们只能往nlogn的方向去思考,我们发现对于每一对同学,要么1A2B,要么1B2A,所以对于每一个同学来说,可以把和他组队的同学分为两类人,一类是与他组队时做A题的,一类是与他组队时做B题的,于是我们只需要定义一个巧妙的排序方式,结构体的属性为a,b,排序方式为
bool cmp(const data &a,const data &b)
{
return a.a+b.b<a.b+b.a;
}
这样就能让所有同学满足,在他之前与他组队的的都选A题,在他之后与他组队的都选B题,再用一个前缀和算出所有选手得分即可,对于不能组队的选手,只要直接减去就可以。
坑点
排序之后下标不是原下标的问题,debug大概三分钟。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<<x<<endl
typedef long long ll;
const int maxn = 3e5+10;
struct data
{
ll A,B,ans;
int id;
}x[maxn];
bool cmp(const data &a,const data &b)
{
return a.A+b.B<a.B+b.A;
}
ll Ans[maxn];
int mp[maxn];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&x[i].A,&x[i].B);
x[i].id=i;
}
sort(x+1,x+1+n,cmp);
ll suma=0,sumb=0;
for(int i=1;i<=n;i++) sumb+=x[i].B;
for(int i=1;i<=n;i++)
{
sumb-=x[i].B;
x[i].ans=suma+(i-1)*x[i].B;
x[i].ans+=x[i].A*(n-i)+sumb;
suma+=x[i].A;
}
for(int i=1;i<=n;i++) mp[x[i].id]=i;
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
u=mp[u];v=mp[v];
if(cmp(x[u],x[v]))
{
x[u].ans-=(x[u].A+x[v].B);
x[v].ans-=(x[u].A+x[v].B);
}
else
{
x[u].ans-=(x[u].B+x[v].A);
x[v].ans-=(x[u].B+x[v].A);
}
}
for(int i=1;i<=n;i++) Ans[x[i].id]=x[i].ans;
for(int i=1;i<=n;i++) printf("%lld ",Ans[i]);
return 0;
}