题面

在一个地区的选举中,共有V个人参加了投票,每一票只可能投给N个政党中的一个。当地的议会共有M个席位。不妨将N个政党编号为1到N,并且设编号为i的政党最终的得票为Vi,则议会中的席位按如下规则分配: 1、将得票数小于总选票的5%的政党剔除。 2、初始时议会为空,每个政党都只有0个席位。 3、对于每个政党P,计算一个参数Qp = Vp / (Sp + 1),Vp为政党P的最终得票,Sp为政党P当前已经在议会拥有的席位。 4、给Qp最大的政党分配一个席位,如果有多个政党的Qp相同,则将席位分给其中编号最小的政党。 5、重复3和4,直到议会已满。 由于计票还没有结束,现在我们只知道一部分选票的投票结果。给出V、N、M以及每个政党当前的得票,请你计算每个政党最多以及最少能赢得多少个席位。

题解

记剩下所有票数为sumsumsum。

xxx位置上的的最大值就是把sumsumsum全部给xxx,然后暴力枚举就行了。每次时间复杂度O(n2)O(n^2)O(n2),总时间复杂度O(n3)O(n^3)O(n3)。

下面是求xxx的最小值。方法是二分,假设二分的值是midmidmid,问题就转化为是否能让剩下的人凑足m−midm-midm−mid个席位。可以发现只有票数≥5%\geq 5\%≥5%的人才可能获得席位。所以只有票数前202020才可能获得席位。按贪心的思路,一定是从大到小排序后去前202020个来凑足m−midm-midm−mid个席位。

设f[i][j]f[i][j]f[i][j]表示用前iii个占领jjj个席位所需要的最少额外票数,那么转移为:

f[i][j]=min⁡k=0jf[i−1][j−k]+cost(i,k)f[i][j]=\min_{k=0}^jf[i-1][j-k]+cost(i,k)f[i][j]=k=0minj​f[i−1][j−k]+cost(i,k)cost(i,k)cost(i,k)cost(i,k)表示让第iii个位置的人占领kkk个席位所需要的最小票数。

要在xxx占领midmidmid个席位的情况下,让第iii个位置的人占领kkk个席位,需要满足的条件有:

  • (a[x]mid+1<a[i]k)(\frac{a[x]}{mid+1}< \frac{a[i]}{k})(mid+1a[x]​<ka[i]​) 或者 (a[x]mid+1=a[i]k&& i的编号<x的编号)(\frac{a[x]}{mid+1}= \frac{a[i]}{k}\&\&\ i的编号<x的编号)(mid+1a[x]​=ka[i]​&& i的编号<x的编号)
  • a[i]+cost(i,k)≥V∗5%a[i]+cost(i,k)\geq V*5\%a[i]+cost(i,k)≥V∗5%

那么直接转移就行了。每次时间复杂度O(20∗m2)O(20*m^2)O(20∗m2),总时间复杂度O(20∗m2log⁡m)O(20*m^2\log m)O(20∗m2logm)

CODE

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
inline void rd(int &x) {
char ch; int flg=1; while(!isdigit(ch=getchar()))if(ch=='-')flg=-flg;
x = 0; do x=x*10+ch-'0'; while(isdigit(ch=getchar())); x*=flg;
}
const int MAXN = 205;
int n, m, V, sum, a[MAXN], c[MAXN], id[MAXN];
inline bool cmp(int i, int j) { return a[i] > a[j]; }
int ans[MAXN], s[MAXN];
inline void Solve_mx() { //暴力枚举模拟
for(int i = 1; i <= n; ++i) {
a[i] += sum;
for(int j = 1; j <= n; ++j) s[j] = 0;
for(int j = 1; j <= m; ++j) {
int x = -1;
for(int k = 1; k <= n; ++k) if(a[k]*20 >= V) {
if(x == -1 || a[k]*(s[x]+1) > a[x]*(s[k]+1)) x = k;
}
if(~x) ++s[x];
}
printf("%d%c", s[i], " \n"[i==n]);
a[i] -= sum;
}
}
int f[21][MAXN];
inline bool check(int x, int mid) {
int cur = 0; for(int i = 0; i <= m; ++i) f[cur][i] = sum+1;
f[cur][0] = 0;
for(int i = 1; i <= n && i <= 20; ++i) {
if(i == x) continue; cur ^= 1;
for(int j = 0; j <= m; ++j) {
f[cur][j] = sum+1;
for(int k = 0; k <= j; ++k) {
int delta = (k * a[x] + mid) / (mid + 1) - a[i]; //向上取整
if(k * a[x] % (mid + 1) == 0 && id[x] < id[i] && k) ++delta; //判断在原数列编号大小
delta = delta < 0 ? 0 : delta;
if(k && (a[i]+delta)*20 < V) delta += (V - (a[i]+delta)*20 + 19) / 20; //凑足5%
f[cur][j] = min(f[cur][j], f[cur^1][j-k] + delta);
}
}
}
return f[cur][m-mid] <= sum;
}
inline int getmin(int x) {
if(a[x]*20 < V) return 0;
int l = 0, r = m, mid;
while(l < r) {
mid = (l + r) >> 1;
if(check(x, mid)) r = mid;
else l = mid+1;
}
return l;
} inline void Solve_mn() {
for(int i = 1; i <= n; ++i) id[i] = i, c[i] = a[i];
sort(id + 1, id + n + 1, cmp); //从大到小排序
for(int i = 1; i <= n; ++i) a[i] = c[id[i]];
for(int i = 1; i <= n; ++i) ans[id[i]] = getmin(i);
for(int i = 1; i <= n; ++i) printf("%d%c", ans[i], " \n"[i==n]);
}
int main () {
rd(V), rd(n), rd(m), sum = V;
for(int i = 1; i <= n; ++i) rd(a[i]), sum -= a[i];
Solve_mx();
Solve_mn();
}

BZOJ 1181: [CROATIAN2009] IZBROI选举(二分+dp)的更多相关文章

  1. 二分+DP HDU 3433 A Task Process

    HDU 3433 A Task Process Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/ ...

  2. [BZOJ 4033] [HAOI2015] T1 【树形DP】

    题目链接:BZOJ - 4033 题目分析 使用树形DP,用 f[i][j] 表示在以 i 为根的子树,有 j 个黑点的最大权值. 这个权值指的是,这个子树内部的点对间距离的贡献,以及 i 和 Fat ...

  3. hdu 3433 A Task Process 二分+dp

    A Task Process Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  4. 2018.10.24 NOIP模拟 小 C 的数组(二分+dp)

    传送门 考试自己yyyyyy的乱搞的没过大样例二分+dp二分+dp二分+dp过了606060把我自己都吓到了! 这么说来乱搞跟被卡常的正解比只少101010分? 那我考场不打其他暴力想正解血亏啊. 正 ...

  5. 「学习笔记」wqs二分/dp凸优化

    [学习笔记]wqs二分/DP凸优化 从一个经典问题谈起: 有一个长度为 \(n\) 的序列 \(a\),要求找出恰好 \(k\) 个不相交的连续子序列,使得这 \(k\) 个序列的和最大 \(1 \l ...

  6. 【bzoj1044】[HAOI2008]木棍分割 二分+dp

    题目描述 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且 ...

  7. Bzoj 1055: [HAOI2008]玩具取名 (区间DP)

    Bzoj 1055: [HAOI2008]玩具取名 (区间DP) 题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1055 区间动态规划和可 ...

  8. Luogu P2511 [HAOI2008]木棍分割 二分+DP

    思路:二分+DP 提交:3次 错因:二分写萎了,$cnt$记录段数但没有初始化成$1$,$m$切的次数没有$+1$ 思路: 先二分答案,不提: 然后有个很$naive$的$DP$: 设$f[i][j] ...

  9. [BZOJ 3992] [SDOI 2015] 序列统计(DP+原根+NTT)

    [BZOJ 3992] [SDOI 2015] 序列统计(DP+原根+NTT) 题面 小C有一个集合S,里面的元素都是小于质数M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数 ...

随机推荐

  1. SpringMVC常用注解@Controller,@Service,@repository,@Component,@Autowired,@Resource,@RequestMapping

    1.controller层使用@Controller注解-用于呈现层,(spring-mvc) @Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controlle ...

  2. python 之 面向对象(多态性、装饰器方法 内置函数补充)

    7.6 多态性 1 什么是多态性 多态指的是同一种事物多种形态,在程序中用继承可以表现出多态.多态性:可以在不用考虑对象具体类型的前提下而直接使用对象下的方法 2.为什要用多态 用基类创建一套统一的规 ...

  3. sequence(线段树+单调栈) (2019牛客暑期多校训练营(第四场))

    示例: 输入: 31 -1 11 2 3 输出: 3 题意:求最大的(a区间最小值*b区间和) 线段树做法:用单调栈求出每个数两边比b数组大的左右边界,然后用线段树求出每段区间的和sum.最小前缀ls ...

  4. Composer简介

    Composer 是 PHP 的一个依赖管理工具.它允许你申明项目所依赖的代码库,它会在你的项目中为你安装他们. 依赖管理 Composer 不是一个包管理器.是的,它涉及 "package ...

  5. create-react-app中的一些功能配置

    1. 根路径别名@ 1. npm run eject调出配置文件.找到webpack.config.js,搜索到,alias,在下面添加一行键值对'@':paths.appSrc, alias: { ...

  6. Student's Camp CodeForces - 708E (dp,前缀和优化)

    大意: $n$行$m$列砖, 白天左侧边界每块砖有$p$概率被摧毁, 晚上右侧边界有$p$概率被摧毁, 求最后上下边界连通的概率. 记${dp}_{i,l,r}$为遍历到第$t$行时, 第$t$行砖块 ...

  7. NetLink通信原理研究、Netlink底层源码分析、以及基于Netlink_Connector套接字监控系统进程行为技术研究

    1. Netlink简介 0x1:基本概念 Netlink是一个灵活,高效的”内核-用户态“.”内核-内核“.”用户态-用户态“通信机制.通过将复杂的消息拷贝和消息通知机制封装在统一的socket a ...

  8. Express配置ssl证书,为网站开启https

    本文不对express多做介绍,下面直奔主题: 一.下载证书(以腾讯云为例): 解压下载的压缩包,找到Nginx文件夹,里面有两个以crt和key结尾的文件,在你的项目根目录新建名为https的空文件 ...

  9. vmware vcsa-故障1

    1.重启vcsa后不能登陆webclient 做实验得时候重启vcsa后不能登陆 web client 开启vcsa直接进入命令模式,命令行登陆后提示:failed to connect  to se ...

  10. Spring Cloud Alibaba学习笔记(14) - Spring Cloud Stream + RocketMQ实现分布式事务

    发送消息 在Spring消息编程模型下,使用RocketMQ收发消息 一文中,发送消息使用的是RocketMQTemplate类. 在集成了Spring Cloud Stream之后,我们可以使用So ...