Problem
一共有
V
V
张投票, 个政党,
m
m
个席位。现在每个政党有 张票。对于每个政党,问通过给这个
n
n
个政党分配剩下的票,使得这个政党得到最多、最少席位是多少个。
席位得分配:对于所有政党,计算一次值:票数(席位数
+1
+
1
),然后给值最大的政党一个席位(若值相同,则编号小的政党获得)。重复此过程,直到席位分配完。
Solution
首先我们看到,最大值肯定是把所有票都给这个政党,然后枚举一下即可。
那么最小值呢?
我们二分查找。
定义
f[i][j]
f
[
i
]
[
j
]
表示前
i−1
i
−
1
个人获得
j
j
席位所需的最小票数
则转移为 第i明获得
j−k
j
−
k
个席位所需票数)
注意:
- 注意 5 5 % 的限制,因此最多有20个席位
- 计算 票数(席位数 +1 + 1 ) 时,要使用 x1y1 x 1 y 1 和 x2y2 x 2 y 2 比较,等同于 x1∗y2 x 1 ∗ y 2 和 x2∗y1 x 2 ∗ y 1 。 double d o u b l e 会炸【捂脸】
转移时因为是要求最小,所有在二分前我们应把数列进行排序(从大到小)
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 210
#define ll long long
#define inf 1<<30
int V,n,m,sum=0,a[N],c[N],b[N],ans[N];
inline bool cmp(int x,int y){return a[x]>a[y];}
int f[22][N];
//f[i][j] 表示前i-1个政党得到j个席位需要的最少票数
inline bool check(int x,int mid){
for(int i=0;i<=m;i++) f[0][i]=f[1][i]=inf;
f[0][0]=0;int pre=0;
for(int i=1;i<=min(n,21);i++){
if(i==x) continue;
for(int j=0;j<=m;j++){
f[pre^1][j]=inf;
for(int k=0;k<=j;k++){
int xx=(a[x]*k+mid)/(mid+1)-a[i];
if(a[x]*k%(mid+1)==0 && b[i]>b[x] && k) xx++;
xx=max(xx,0);
if(k && (a[i]+xx)*20<V) xx+=(V+19-20*(a[i]+xx))/20;
f[pre^1][j]=min(f[pre^1][j],f[pre][j-k]+xx);
}
}
pre^=1;
}
if(f[pre][m-mid]<=V-sum) return 1;
return 0;
}
inline int getmin(int id){
if(a[id]*20<V) return 0;
int l=0,r=m,k=0;
while(l<=r){
int mid=l+r>>1;
if(check(id,mid)) r=mid-1,k=mid;
else l=mid+1;
}
return k;
}
int main(){
freopen("a.in","r",stdin);
scanf("%d%d%d",&V,&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
for(int i=1;i<=n;i++){
a[i]+=V-sum;int x=0;
for(int j=1;j<=m;j++){
int mx=0;
for(int k=1;k<=n;k++){
if(a[k]*20<V) continue;
if(!mx || a[k]*(c[mx]+1)>a[mx]*(c[k]+1)) mx=k;
}
c[mx]++;
}
printf("%d ",c[i]);
memset(c,0,sizeof(c));
a[i]-=V-sum;
}puts("");
memcpy(c,a,sizeof(a));
for(int i=1;i<=n;i++) b[i]=i;
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;i++) a[i]=c[b[i]];
for(int i=1;i<=n;i++){
ans[b[i]]=getmin(i);
}
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}