题解:P8364 [COI2009] IZBORI

posted on 2024-12-24 06:09:10 | under | source

初看此题,一头雾水,思绪混乱,不可做啊!故打算写一篇题解造福后人。

\(Q_{i,j}\) 表示党派 \(i\) 已经获得 \(j\) 个席位时的分数。

求最大值直接把所有票送给它,然后模拟即可。

求最小值,貌似难以下手,因为难以刻画每次取最大值这一过程。不妨多确定一些信息,枚举当前 \(x\) 党派最终获得 \(\le p\) 个席位。显然是有单调性的,可以二分。

问题转化为:能否为其它党派分配票,满足 \(x\) 党派获得席位 \(\le p\),且其它党派获得的席位总和 \(\ge m-mid\)

假如已经确定其它党派获得了多少票,如何求出其席位总和?由于会互相影响,还是不好搞。不妨从简单情况入手,假如只有 \(1\) 个其它党派 \(y\),那么它获得的席位 \(p\) 满足 \(Q_{y,p-1} \ge Q_{x,mid}\),否则它必然有一个席位会被 \(x\) 抢走,导致 \(x\) 获得 \(>mid\) 个席位。

然后你发现很美妙的一点是:并不关心其它党派之间取得席位的顺序,因为我们只关心其他党派总共可以取得多少个席位。也就是说,只需按照前文的方式,分别计算每个党派取得的席位再加起来即可。

直接背包即可。现在有了个基于票数的 dp,能否优化?一个经典思想是换维,可以设 \(f_{i,j}\) 表示考虑前 \(i\) 个党派,让它们共取得 \(\ge j\) 个席位至少要花费多少票。转移的话可以解不等式得出让 \(i\) 党派获得至少 \(j\) 个席位最少多少票。

还是会炸。但你注意到只有票数不小于总票数的 \(\frac 1{20}\) 的党派才会获得席位,所以只看 \(20\) 个党派即可。显然要尽量把票给基础票数较大的党派,于是就能过了。复杂度 \(O(nm^2\log m)\),带常数 \(20\)

注意一个细节:假如 \(x\) 本身就小于总票数的 \(\frac 1{20}\) 那么返回 \(0\) 即可。

不要有畏难心理,一些看起来完全不可做的题,要尝试寻找问题性质。

代码

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N = 2e2 + 5;
int cnt, lim, n, m, na, fwmx, ans[N], f[N], g[N], inf;
struct node{int a, id, s;} p[N], a[N];

inline bool cmp(node A, node B) {return A.a == B.a ? A.id < B.id : A.a > B.a;}
inline int calcmax(int pos){
	for(int i = 1; i <= n; ++i){
		a[i] = p[i];
		if(i == pos) a[pos].a += cnt;
		if(a[i].a * 20 < lim) a[i].id = -1;
	}
	for(int step = 1; step <= m; ++step){
		int mx = 0;
		for(int j = 1; j <= n; ++j){
			if(a[j].id == -1) continue;
			if(!mx || a[j].a * (a[mx].s + 1) > a[mx].a * (a[j].s + 1)) mx = j;
		}
		++a[mx].s;
	}
	return a[pos].s;
}
inline int chk(int pos, int mid){
	memset(f, 0x3f, sizeof f);
	inf = f[0], f[0] = 0;
	for(int i = 1; i <= min(20ll, n); ++i){
		if(i == pos) continue;
		for(int j = 0; j <= m; ++j) g[j] = f[j], f[j] = inf;
		for(int j = 0; j <= m; ++j)
			for(int k = 0; k <= j; ++k){
				int r;
				if(!k) r = 0;
				else{
					/*
					(a + r) >= lim / 20
					i < pos: (a + r) / k >= pos.a / (mid + 1)
					i > pos: (a + r) / k > pos.a / (mid + 1)
					*/ 
					r = max(0ll, (lim + 19) / 20 - p[i].a);
					if(p[i].id < p[pos].id) r = max(r, (k * p[pos].a + mid) / (mid + 1) - p[i].a); 
					else r = max(r, (k * p[pos].a) / (mid + 1) - p[i].a + 1);
				} 
				f[j] = min(f[j], g[j - k] + r); 
			}
	}
	return f[m - mid] <= cnt;
}
inline int calcmin(int pos){ 
	if(p[pos].a * 20 < lim) return 0; //pay attention to this
	int L = -1, R = m, mid;
	while(L + 1 < R){
		mid = (L + R) >> 1;
		if(chk(pos, mid)) R = mid;
		else L = mid;
	} 
	return R;
}
signed main(){
	cin >> cnt >> n >> m;
	lim = cnt;
	for(int i = 1; i <= n; ++i) scanf("%lld", &p[i].a), p[i].id = i, cnt -= p[i].a;
	for(int i = 1; i <= n; ++i) printf("%lld ", calcmax(i));
	putchar('\n');
	sort(p + 1, p + 1 + n, cmp);
	for(int i = 1; i <= n; ++i) ans[p[i].id] = calcmin(i);
	for(int i = 1; i <= n; ++i) printf("%lld ", ans[i]);
	return 0;
}
/*
1000 14 50
9 54 71 16 88 150 148 29 34 47 39 44 30 57
*/
posted @ 2026-01-15 08:19  Zwi  阅读(1)  评论(0)    收藏  举报