题解:P8364 [COI2009] IZBORI
初看此题,一头雾水,思绪混乱,不可做啊!故打算写一篇题解造福后人。
记
求最大值直接把所有票送给它,然后模拟即可。
求最小值,貌似难以下手,因为难以刻画每次取最大值这一过程。不妨多确定一些信息,枚举当前
问题转化为:能否为其它党派分配票,满足
假如已经确定其它党派获得了多少票,如何求出其席位总和?由于会互相影响,还是不好搞。不妨从简单情况入手,假如只有
然后你发现很美妙的一点是:并不关心其它党派之间取得席位的顺序,因为我们只关心其他党派总共可以取得多少个席位。也就是说,只需按照前文的方式,分别计算每个党派取得的席位再加起来即可。
直接背包即可。现在有了个基于票数的 dp,能否优化?一个经典思想是换维,可以设
还是会炸。但你注意到只有票数不小于总票数的
注意一个细节:假如
不要有畏难心理,一些看起来完全不可做的题,要尝试寻找问题性质。
代码
#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
*/