P8364 题解
题面
题意是比较清楚的。这里提供一种比较好想的 DP 做法。
对于第一小问,我们求解每个政党的时候都假设它获得了所有未知的选票,然后模拟计算即可。
对于第二小问,我们首先注意到两个事实:
- 如果初始票数小于总票数的 \(5\%\),那么这个政党不会有任何席位。
- 如果这个政党被分配到 \(m\) 个席位,那么他也能被分配到 \(m+1\) 个席位,前提条件是 \(m\) 严格小于第一问的答案。
那么我们不难想到使用二分来搜出答案的最小值。下面着重说一下 \(\operatorname{check}\) 函数。
我们记 \(dp_{i,j}\) 来表示顺序前 \(i\) 个党派(不包括它本身)分配了 \(j\) 个席位需要的最小票数,那么 \(\operatorname{check}(idx,seats)=[dp_{-1,M-seats}\le V-sum]\),其中 \(-1\) 表示 \(dp\) 数组的最后一项, \(sum\) 表示初始时所有票数的和。
考虑从 \(dp_{i-1,j-k}\) 怎么转移到 \(dp_{i,j}\),那么考虑需要多分配的票数 \(extra=\frac{votes_{idx}\times k+seats}{seats+1}-votes_i\),为了避免票数相同但编号较小的情况,因此这里需要特判;为了避免不超过 \(5\%\),这里也需要特判。因此我们不难写出下面的(伪)代码:
extra = (votes[idx] * k + seats) / (seats + 1) - votes[i];
extra = max(extra + (votes[idx] * k % (seats + 1) == 0 and rk[i] > rk[idx] ans k), 0);
if (k and (votes[i] + extra) * 20 < V )
extra += (V + 19 - 20 * (votes[i] + extra)) / 20;
dp[i-1][j-k]->dp[i][j]
考虑优化。
- 对于空间的优化,我们显然可以使用滚动数组
- 对于时间的优化,考虑到真正产生影响的不会超过 \(20\) 个政党,因此我们可以在最开始的时候对所有政党进行排序,最多只取前 \(20\) 个政党。另一方面,处于相同的原因,我们最多只会判断 \(20\) 个政党,其余的都没有席位。
优化以后的时间复杂度是 \(O(m^2\log m+n)\) 的,但有 \(20\) 倍常数。
代码如下:
#include <bits/stdc++.h>
using namespace std;
int V, N, M, nowVotes;
int vts[101];
vector<pair<int, int> > __;
int votes[101], dp[2][201], rk[101];
int sol[101];
int check(int idx, int seats) {
int ii = 0;
if (votes[idx] * 20 < V)
return 1;
for (int i = 0; i <= 200; i++)
dp[0][i] = 0x3f3f3f3f;
dp[0][0] = 0;
for (int i = 0; i < min(N, 21); i++)
if (i != idx) {
ii ^= 1;
for (int j = 0; j <= M; j++) {
dp[ii][j] = 0x3f3f3f3f;
for (int k = 0; k <= j; k++) {
int extra = (votes[idx] * k + seats) / (seats + 1) - votes[i];
extra = max(extra + (votes[idx] * k % (seats + 1) == 0 && rk[i] > rk[idx] && k), 0);
if (k && (votes[i] + extra) * 20 < V) {
extra += (V + 19 - 20 * (votes[i] + extra)) / 20;
}
dp[ii][j] = min(dp[ii ^ 1][j - k] + extra, dp[ii][j]);
}
}
}
return dp[ii][M - seats] <= V - nowVotes;
}
int main() {
scanf("%d %d %d", &V, &N, &M);
__.resize(N);
for (int i = 0; i < N; i++) {
scanf("%d", &votes[i]);
__.push_back({-votes[i], i});
nowVotes += votes[i];
}
sort(__.begin(), __.end());
for (int i = 0; i < N; i++) {
rk[i] = __[i].second;
votes[i] = -__[i].first;
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++)
vts[j] = 0;
votes[i] += V - nowVotes;
for (int j = 0; j < M; j++) {
int max_a = 0, max_b = 1, maxParty = 0;
for (int k = 0; k < N; k++)
if (votes[k] * 20 >= V) {
if (votes[k] * max_b > max_a * (vts[k] + 1)) {
max_a = votes[k];
max_b = vts[k] + 1;
maxParty = k;
}
if (votes[k] * max_b == max_a * (vts[k] + 1) && rk[maxParty] > rk[k]) {
max_a = votes[k];
max_b = vts[k] + 1;
maxParty = k;
}
}
vts[maxParty]++;
}
sol[rk[i]] = vts[i];
votes[i] -= V - nowVotes;
}
for (int i = 0; i < N; i++) {
printf("%d ", sol[i]);
}
printf("\n");
for (int i = 0; i < N; i++) {
if (votes[i] * 20 < V) {
sol[rk[i]] = 0;
continue;
}
int ll = 0, rr = sol[rk[i]];
while (ll < rr) {
int mid = ll + ((rr - ll) >> 1);
if (check(i, mid))
rr = mid;
else
ll = mid + 1;
}
sol[rk[i]] = ll;
}
for (int i = 0; i < N; i++) {
printf("%d ", sol[i]);
}
printf("\n");
return 0;
}

浙公网安备 33010602011771号