CodeForces730E Award Ceremony(拓扑排序+结论)

CF730E. Award Ceremony

problem

题目链接

题目大意:

给出 n n n 个队封榜时的榜单 a i a_i ai 和揭榜时的变化情况 d i d_i di

揭榜时,这个队的名次会变化 t i t_i ti

注意在别的队揭榜时,自己队的排名也是动态变化的。

计算的变化名次是自己揭榜前后的名次差,而非最终排名与最初排名差。

然后问如何安排揭榜顺序,使得 s u m ( ∣ t i ∣ ) sum(|t_i|) sum(ti) 最大。

1 ≤ n ≤ 100 , 1 ≤ a i ≤ 100 , − 100 ≤ d i ≤ 100 1≤n≤100,1≤ai≤100, -100≤di≤100 1n1001ai100,100di100

solution

考虑两两队伍之间的排名变化贡献。

  • 如果两个队伍揭榜前后相对排名顺序发生变化,则无论怎么安排两个队伍的揭榜先后,答案贡献都是 1 1 1

    i.e. 假设 i i i 排名在 j j j 前面( i < j i<j i<j),揭榜后 j j j 排名更靠前 i > j i>j i>j

    有两种情况。

    • i i i 分数下降, j j j 可能上升也可能下降,但 i i i 一定下降地更猛。
      • 假设先揭榜 i i i ,则 i i i 排名变成 j j j 后面,贡献为 1 1 1,后面揭榜 j j j,依旧在 i i i 之前。
      • 假设先揭榜 j j j,则 j j j 可能已经超过 i i i ,后面揭榜仍在 i i i 前面;也可能还是没有超过 i i i,但是 i i i 揭榜后就掉在 j j j 后面了。
    • i i i 分数上升, j j j 必须上升,且 j j j 一定上升地更猛。假设揭榜过程与上面一样。不再赘述。
  • 如果两个队伍揭榜前后相对顺序并未发生改变。则有两种情况。

    • 不管先揭榜谁,都保持着 i < j i<j i<j。则对答案无贡献。
    • i i i j j j 超过后又再次超过了 j j j。答案就是 2 2 2

按照上述方法会得到一个有向无环图,根据拓扑排序就能得到揭晓的顺序,但是本题并未做要求。

只要最终改变的最大值结果,那么就直接枚举两个队伍,看属于上面的哪种情况贡献。计算即可。

时间复杂度为 O ( n 2 ) O(n^2) O(n2)

证明这样的做法得到的关系图不存在环。

只有第二种情况的第二点,贡献为 2 2 2,这种 case 会需要考虑两个队伍揭榜的先后顺序。

即只有这种 case 才会在图上增加一条边,因此环只可能在这个地方产生。

假设三个队伍 i , j , k i,j,k i,j,k 构成环。

首先因为两个队伍之间有边,所以有 i i i 揭榜后能超过 j j j ,然后又被 j j j 揭榜后反超,这样贡献才为 2 2 2

( j , k ) , ( k , i ) (j,k),(k,i) (j,k),(k,i) 同样也是如此。

j j j 揭榜后超过 k k k 又被 k k k 反超, k k k 揭榜后超过 i i i 又被 i i i 反超。

这三个揭榜顺序必然是确定的,所以名次改变方向是固定的。

( i , j ) ( j , k ) (i,j)(j,k) (i,j)(j,k) 之间的关系就知道 k k k 揭榜后超过 i , j i,j i,j 不可能又让 i i i 再揭榜一次反超。

所以这个情况必然是如图所示:

在这里插入图片描述

code

#include <bits/stdc++.h>
using namespace std;
#define maxn 105
struct node { 
    int val, id, tag;
    bool operator < ( node &t ) {
        return val == t.val ? id < t.id : val > t.val;
    }
}a[maxn];
int n, ans;

int calc( int x, int y ) {
    node lst_x = a[x], lst_y = a[y];
    node new_x = lst_x, new_y = lst_y;
    new_x.val += lst_x.tag;
    new_y.val += lst_y.tag;
    if( lst_x < lst_y and new_y < new_x ) return 1;
    if( lst_y < lst_x and new_x < new_y ) return 1;
    if( lst_x < lst_y and new_x < new_y and new_y < lst_x ) return 2;
    if( lst_x < lst_y and new_x < new_y and lst_y < new_x ) return 2;
    if( lst_y < lst_x and new_y < new_x and new_x < lst_y ) return 2;
    if( lst_y < lst_x and new_y < new_x and lst_x < new_y ) return 2;
    return 0;
}

int main() {
    scanf( "%d", &n );
    for( int i = 1;i <= n;i ++ ) scanf( "%d %d", &a[i].val, &a[i].tag ), a[i].id = i;
    sort( a + 1, a + n + 1 );
    for( int i = 1;i <= n;i ++ )
        for( int j = i + 1;j <= n;j ++ )
            ans += calc( i, j );
    printf( "%d\n", ans );
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值