1. 程式人生 > >【Codeforces Round #519 by Botan Investments - E. Train Hard, Win Easy】排序+字首和

【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 , B 已知每個學生回答A,B問題可得到的分值
1 2 每一次組隊考試學生1選擇一道題,學生2選擇另一道題
m 現在想知道每個人的最終最小得分為多少(有m組不可以組隊的同學,其他同學兩兩之間必須組一次隊)。
n < = 3 1 0 5 , m < = 3 1 0 5 n<=3*10^5,m<=3*10^5

做法

由於這道題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;
}