题解 [JOI 2022 Final] 选举
我以为我爆标了结果最后假的离谱
考虑这个协作者的性质
发现一定是越先选越优
为了提升加倍的效果协作者一定是最先选的,且一定按 \(b_i\) 不降选
所以可以先按 \(b\) 排序
那么我假就假在这里了:
我认为存在一个位置 \(i\) 满足选的协作州都在 \(i\) 之前(包括),支持州都在 \(i\) 之后
假的显然,支持州可以在两个协作州之间
但是发现两个协作州之间不会有反对州
所以对这个令 \(f_{i, j}\) 为前 \(i\) 个州有 \(j\) 个协作州最小时间
外层枚举最后有多少个协作者,内层枚举最后一个协作州的位置
将后面的州按 \(a\) 排序了贪心选
这样就是 \(O(n^3\log n)\) 的
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 510
#define fir first
#define sec second
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, k;
int sta[N], top;
pair<int, int> a[N];
double f[N][N], ans=1e20;
signed main()
{
n=read(); k=read();
for (int i=1; i<=n; ++i) {
a[i].fir=read(), a[i].sec=read();
if (a[i].sec==-1) a[i].sec=INF;
}
sort(a+1, a+n+1, [](pair<int, int> a, pair<int, int> b){return a.sec==b.sec?a.fir<b.fir:a.sec<b.sec;});
for (int t=0; t<=n; ++t) {
memset(f, 0x43, sizeof(f));
f[0][0]=0;
for (int i=0; i<=n; ++i) {
for (int j=0; j<=min(i, t); ++j) {
if (i) f[i][j]=min(f[i][j], f[i-1][j]+(1.0*a[i].fir)/(t+1.0));
if (j) f[i][j]=min(f[i][j], f[i-1][j-1]+(1.0*a[i].sec)/(1.0*j));
}
top=0;
for (int j=i+1; j<=n; ++j) sta[++top]=a[j].fir;
sort(sta+1, sta+top+1);
for (int j=1; j<=top; ++j) sta[j]+=sta[j-1];
ans=min(ans, f[i][t]+sta[k-i]/(t+1.0));
}
}
printf("%.10lf\n", ans);
return 0;
}