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 1≤n≤100,1≤ai≤100,−100≤di≤100。
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
i
i 分数下降,
j
j
j 可能上升也可能下降,但
i
i
i 一定下降地更猛。
-
如果两个队伍揭榜前后相对顺序并未发生改变。则有两种情况。
- 不管先揭榜谁,都保持着 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;
}