第18回日本情報オリンピック 本選 コイン集め 解説
第18回日本情報オリンピック 本選 コイン集め 解説
更好的阅读体验
问题描述
JOI 先生的收藏室里有一张巨大的桌子,上面有许多稀有的硬币。为了清理桌子,他要重新摆放硬币。
桌面可视为
JOI 先生有
现在给出硬币的数量和初始时所在的位置,编写一个程序,计算完成 JOI 先生目标所需的最少操作次数。
题解
贪心,结论类似于 [JSOI2018]列队 / 均分纸牌。
注意到最后所有的
所以 Subtask1 的
下面考虑如何分配格子。
如果想要将这些棋子,分配到这连在一起的一个矩形中,那么这些棋子首先需要移动到这个矩形中,显然对于棋子
移动顺序显然不影响答案,所以行列分开考虑,分别有以下几种情况:
将棋子
上述移动方法之所以正确,是因为在移动过程中,每个格子可以同时堆放多个棋子。
但是可能存在多个不同的
假设
显然,
分几类讨论:
-
g(i,1)=g(i,2)=0
无答案贡献。
-
g(i,1)>0,g(i,2)<0
令
此后,需要把
第
-
g(i,1)<0,g(i,2)>0
同上类。
-
g(i,1) \times g(i,2) > 0
同上类,只是不需要计算
Code
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
template < typename Tp >
inline void rd(Tp &x) {
x = 0; int fh = 1; char ch = 1;
while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if(ch == '-') fh = -1, ch = getchar();
while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
x *= fh;
}
template < typename Tp >
inline void bird(Tp &x) {
x = 0; int fh = 1; char ch = 1;
while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if(ch == '-') fh = -1, ch = getchar();
while(ch >= '0' && ch <= '9') x = x * 2 + ch - '0', ch = getchar();
x *= fh;
}
template < typename Tp >
inline void smax(Tp &x, Tp y) {
if(x < y) x = y;
}
const int maxn = 200000 + 7;
int n;
int num[2][maxn];
long long ans;
//#define tester
signed mian(void) {
#ifdef tester
freopen("coin.in", "r", stdin);
freopen("coin.out", "w", stdout);
#endif
rd(n);
for(int i = 1, x, y; i <= n * 2; i++) {
rd(x); rd(y);
int sx, sy;
if(x >= n) sx = n;
else if(x <= 1) sx = 1;
else sx = x;
if(y > 1) sy = 2;
else sy = 1;
if(sx > x) ans += sx - x;
else ans += x - sx;
if(sy > y) ans += sy - y;
else ans += y - sy;
--sy;
num[sy][sx]++;
}
for(int i = 1; i <= n; i++) {
num[0][i]--, num[1][i]--;
int RIP = 0;
if(num[0][i] > 0 && num[1][i] < 0) RIP = min(num[0][i], -num[1][i]);
else if(num[0][i] < 0 && num[1][i] > 0) RIP = min(-num[0][i], num[1][i]);
ans += RIP;
if(num[0][i] > 0 && num[1][i] < 0) num[0][i] -= RIP, num[1][i] += RIP;
else if(num[0][i] < 0 && num[1][i] > 0) num[0][i] += RIP, num[1][i] -= RIP;
num[0][i + 1] += num[0][i], num[1][i + 1] += num[1][i];
if(num[0][i] > 0) ans += num[0][i];
else ans += -num[0][i];
if(num[1][i] > 0) ans += num[1][i];
else ans += -num[1][i];
}
printf("%lld\n", ans);
return 0;
}