【Codeforces Round #519 by Botan Investments - E. Train Hard, Win Easy】排序+前缀和

Codeforces Round #519 by Botan Investments - E. Train Hard, Win Easy

题意

有 两 门 考 试 A , B , n 个 学 生 要 两 两 组 队 参 加 这 场 考 试 有两门考试A,B,n个学生要两两组队参加这场考试 A,Bn
每 场 考 试 对 两 个 人 的 加 分 均 为 两 个 问 题 的 得 分 总 和 每场考试对两个人的加分均为两个问题的得分总和
已 知 每 个 学 生 回 答 A , B 问 题 可 得 到 的 分 值 已知每个学生回答A,B问题可得到的分值 A,B
每 一 次 组 队 考 试 学 生 1 选 择 一 道 题 , 学 生 2 选 择 另 一 道 题 每一次组队考试学生1选择一道题,学生2选择另一道题 12
现 在 想 知 道 每 个 人 的 最 终 最 小 得 分 为 多 少 ( 有 m 组 不 可 以 组 队 的 同 学 , 其 他 同 学 两 两 之 间 必 须 组 一 次 队 ) 。 现在想知道每个人的最终最小得分为多少(有m组不可以组队的同学,其他同学两两之间必须组一次队)。 m
n &lt; = 3 ∗ 1 0 5 , m &lt; = 3 ∗ 1 0 5 n&lt;=3*10^5,m&lt;=3*10^5 n<=3105,m<=3105

做法

由于这道题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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值