LOJ #3030. 「JOISC 2019 Day1」馕

LOJ 传送门

考虑贪心。发现第 \(i\) 个人获得的愉悦度越接近 \(\frac{\sum\limits_{j=1}^L v_{i,j}}{n}\) 越好,因为给其他人的空间就越多。因此先预处理出每个人将将整个馕划分为 \(n\) 段愉悦值相同部分的端点 \(d_{i,1},d_{i,2},...,d_{i,n-1}\)

构造第 \(j\) 段时考虑每次取最小的 \(d_{i,j}\) 作为断点并把这一段分给第 \(i\) 个人。因为上一个断点 \(\le d_{i,j-1}\)(每次取最小),所以很神奇地满足了条件。

code
// Problem: #3032. 「JOISC 2019 Day1」馕
// Contest: LibreOJ
// URL: https://loj.ac/p/3032
// Memory Limit: 268 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef __int128 lll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 2020;

ll n, m, a[maxn][maxn], b[maxn], c[maxn], f[maxn];
bool vis[maxn];

struct frac {
	ll x, y;
	frac(ll a = 0, ll b = 1) : x(a), y(b) {}
} d[maxn][maxn];

inline bool operator < (const frac &a, const frac &b) {
	return (lll)a.x * b.y < (lll)a.y * b.x;
}

void solve() {
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			scanf("%lld", &a[i][j]);
			b[i] += a[i][j];
		}
	}
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			c[j] = n * a[i][j];
		}
		for (int j = 1, k = 1; j < n; ++j) {
			ll t = b[i];
			while (c[k] < t) {
				t -= c[k];
				++k;
			}
			c[k] -= t;
			d[i][j] = frac(k * n * a[i][k] - c[k], n * a[i][k]);
		}
	}
	for (int j = 1; j < n; ++j) {
		int p = -1;
		for (int i = 1; i <= n; ++i) {
			if (!vis[i] && (p == -1 || d[i][j] < d[p][j])) {
				p = i;
			}
		}
		f[j] = p;
		vis[p] = 1;
		printf("%lld %lld\n", d[p][j].x, d[p][j].y);
	}
	for (int i = 1; i <= n; ++i) {
		if (!vis[i]) {
			f[n] = i;
			break;
		}
	}
	for (int i = 1; i <= n; ++i) {
		printf("%lld ", f[i]);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2023-03-10 17:43  zltzlt  阅读(43)  评论(0)    收藏  举报