Title

P9742 「KDOI-06-J」贡献系统 题解

前言

其实这道题的难度真的不高,但是比赛时蒟蒻也是花费了一个小时才想出来,主要考查思维能力和熟练运用基本语法、基础算法的能力。

博客食用更佳

解题思路

这道题要求我们求出在排名变动后能够获得的最大贡献值,那么,我们很容易能够想到,如果有若干个人的排名上升,那么肯定会有至少一人的排名会下降,且这些排名下降的人,在比赛前的排名中,肯定是比排名上升的人靠前的,反之亦然。也就是说,x{1,2,...,n},且 Pxy{1,2,...,x}Py<PxPy,其中 PxPy 表示排名。

因为存在 ci<0,显然,我们要尽量使得 c 为负的人排名下降, c 为正的人排名上升。那么,我们考虑一下两种情况:

  1. 在比赛开始前的排名最后一位的贡献值为正,那么也就说 对于所有的贡献为正的人,必然存在一种方案使得他们的排名下降,而对于那些在贡献为负的人排名后的人,必然存在一种方案使得他们的排名上升,那么,我们只需要找到第一个排名为负的人,把排名在他后面的所有人的贡献的绝对值相加就可以了。

  2. 在比赛开始前的排名最后一位的贡献值为负,那么这个时候我们就需要考虑一下是让哪一个贡献为负的人排名上升。因此,我们需要先找到从后往前数第一个贡献不为负的人,显然,从这个人开始,所有贡献为负的人的排名都可以下降,所有贡献为非负数的人的排名都可以上升。接着,我们计算从最后一个人到这个人的贡献值的绝对值的后缀和,然后枚举从第 n 个人到当前的这个人,每一个人排名上升所能增加的贡献值 w,易得,w=sumfnowsumfi+ci,其中 now 表示从后往前数第一个贡献不为负的人的编号,那么我们可以求出后面这一段(从第一个贡献为负的人到最后一个人)的贡献最大值 Max=firstnow+maxnownw

在处理完后面这一段之后,我们还需要求出前面连续的一段贡献值为正的人的最大贡献值之和,和求 w 的方法相同,先求出第一个人到第 first1 个人的贡献值的前缀和,然后从 1first1 枚举求出贡献值 W 的 最大值,其中 W=sumfirstsumicifirst 表示第一个贡献为负的人的编号,每个人的编号为在在比赛前的排名。

最后,我们的答案 ans=max1nW+Max

AC 代码

#include<bits/stdc++.h>
#define ll long long
#define ull usigned long long
using namespace std;
const string TypideName="c";
inline void readc(char &c){
    c=getchar();
    while(c==' '||c=='\n')
        c=getchar();
}inline void writec(char c){putchar(c);}
template<typename T>inline void read(T& x) {
    if(typeid(x).name()==TypideName){char ch;readc(ch);x=ch;return;}
    x = 0; bool f = false; char ch = getchar();
    while (ch < '0' || ch>'9') { if (ch == '-') f = !f; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar(); }
    x = (f ? -x : x); return;
}template<typename T>inline void put(T x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) put(x / 10);
    putchar(x % 10 + '0'); return;
}template<typename T>inline void write(T x) {
    if(typeid(x).name()==TypideName){writec(x);return;}
    put(x);
}
inline void read(__int128 &x){
    x=0;bool f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=!f;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    x=(f?x:-x);
}inline void put(__int128 x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) put(x/10);
    putchar(x%10+'0');return;
}inline void write(__int128 x){put(x);putchar('\n');}
template<typename T,typename... Args>
inline void read(T& x,Args&...x_) {read(x),read(x_...);}
template<typename T,typename... Args>
inline void write(T x,Args...x_){write(x),write(x_...);}
#define N 200005
int n;
struct OI{
    ll r,c;
    int pos;
}a[N],f[N],z[N];
int cntf,cntz;
ll mint[N],ans=0;
ll sumf[N],sum[N];
inline void work(){
    read(n);cntf=cntz=ans=0;
    for(int i=1;i<=n;i++)
        read(a[i].r);
    for(int i=1;i<=n;i++)
        read(a[i].c);
    for(int i=1;i<=n;i++)
        a[i].pos=i;
    for(int i=1;i<=n;i++)
        if(a[i].c<0)
            f[++cntf]=a[i];
    if(f[cntf].pos==n){
        int now=0;
        for(int i=n;i>=1;i--)
            if(a[i].c>=0){
                now=i;break;
            }
        for(int i=f[1].pos;i<=now;i++)
            ans+=abs(a[i].c);
        memset(sumf,0,sizeof(sumf));
        for(int i=n;i>now;i--)
            sumf[i]=sumf[i+1]-a[i].c;
        ll maxt=0;for(int i=n;i>now;i--)
            if(sumf[now+1]-sumf[i]+a[i].c>maxt)
                maxt=sumf[now+1]-sumf[i]+a[i].c;
        ans+=maxt;
    }else if(cntf>0)
        for(int i=f[1].pos;i<=n;i++)
            ans+=abs(a[i].c);
    int now=f[1].pos-1;
    if(cntf==0) now=n;
    memset(sum,0,sizeof(sum));
    for(int i=1;i<=now;i++)
        sum[i]=sum[i-1]+a[i].c;
    ll maxt=0;
    for(int i=1;i<now;i++){
        if(sum[now]-sum[i]-a[i].c>maxt)
            maxt=sum[now]-sum[i]-a[i].c;
    }ans+=maxt;
    write(ans,'\n');
}
signed main(){
    int T;read(T);
    while(T--)work();
    return 0;
}
posted @   UncleSam_Died  阅读(14)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示