题目大意

  有 \(n\) 堆石子,初始时第 \(i\) 堆石子有 \(a_i\) 个。

  你每次取石子会取 \(k\) 个。在你取完一堆石子之后才能在下一堆中取石子。

  游戏会进行 \(t\) 轮,每轮会发生以下事件:

  • 你可以进行任意次取石子操作。
  • 每堆的石子个数会增加,具体的,第 \(i\) 堆的式子个数会增加 \(b_i\) 个。

  每一堆式子有个上限 \(c_i\),如果在某个时刻,某堆石子的数量超过上限,就就输了。

  求在不会输掉游戏的前提下,你最少进行几次取石子操作。

  \(n,t\leq 200,1\leq k\leq {10}^9,0\leq a_i,b_i\leq c_i\leq {10}^9\)。

题解

  我们可以在最后加一堆 \(a={10}^9,b={10}^9,c={10}^9\times (t+1)\) 的石子堆,这样每次取石子都一定能取到 \(k\) 个。这可以让我们更方便地计算石子个数。

  先考虑 \(a_i=0\) 的情况。

  记 \(sa,sb\) 为 \(a,b\) 的前缀和。

  记 \(f_{i,j}\) 为前 \(i\) 堆石子,进行了 \(j\) 轮游戏,且每次取石子都取了 \(k\) 个的最小操作次数。

  记 \(g_{i,j}\) 为前 \(i\) 堆石子,进行了 \(j\) 轮游戏,再取了若干次石子,每次石子都取了 \(k\) 个,且 \([1,i)\) 的石堆中没有石子的最小操作次数。

  • 如果不取第 \(i\) 种石子也满足要求(即 \(j\times b_i\leq c_i\) 且 \(f_{i-1,j} \neq \infty\)),转移为

    • \(f_{i,j}\leftarrow f_{i-1,j}\)
    • \(g_{i,j}\leftarrow\lceil\frac{j\times sb_{i-1}}{k}\rceil\)(要求 \(\lceil\frac{j\times sb_{i-1}}{k}\rceil\times k\leq j\times sb_i\),因为要有足够多的式子给你取)
  • 否则枚举最后一次取 \(i\) 的时间 \(l\),我们的策略是:

    • 先在前 \(l\) 轮取完 \([0,i)\),再取若干次石子:\(g_{i,l}\)
    • 计算要取多少次第 \(i\) 堆的石子:剩余的石子个数是 \(m=l\times sb_{i}-k\times g_{i,l}\)。为了让第 \(i\) 堆的石子不超过上限,我们还要取 \(x=\lceil\frac{\max(0,m+(j-l)\times b_i-c_i)}{k}\rceil\) 次。如果石子不够(\(x\times k>m\)),则无解。
    • 再决策剩下 \(j-l\) 轮。这部分的贡献和第一种情况类似。

    因此,转移为:

    • \(f_{i,j}\leftarrow g_{i,l}+x+f_{i-1,j-l}\)
    • \(g_{i,j}\leftarrow g_{i,l}+x+\lceil\frac{(j-l)\times sb_{i-1}}{k}\rceil\)

  时间复杂度为 \(O(nt^2)\)。

  \(a_i\neq 0\) 的情况和 \(a_i=0\) 的情况类似,只需要在某些计算石子个数的地方加上 \(a_i\) 即可。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<cmath>
#include<vector>
#include<assert.h>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
using std::vector;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void open2(const char *s){
#ifdef DEBUG
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
void upmin(ll &a,ll b)
{
a=min(a,b);
}
const int N=210;
const ll inf=0x3fffffffffffffffll;
ll f[N][N][2];
ll g[N][N][2];
ll a[N],b[N],c[N],sa[N],sb[N];
ll ceil(ll a,ll b)
{
return (a+b-1)/b;
}
int n,t;
ll k;
int main()
{
open("c");
scanf("%d%d%lld",&n,&t,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
sa[i]=sa[i-1]+a[i];
sb[i]=sb[i-1]+b[i];
}
// printf("%lld\n",sa[n]);
n++;
a[n]=1000000000ll;
b[n]=1000000000ll;
c[n]=1000000000ll*(t+1);
sa[n]=sa[n-1]+a[n];
sb[n]=sb[n-1]+b[n];
for(int i=1;i<=n;i++)
for(int j=1;j<=t;j++)
{
f[i][j][0]=g[i][j][0]=inf;
if(0*a[i]+j*b[i]<=c[i]&&f[i-1][j][0]!=inf)
{
upmin(f[i][j][0],f[i-1][j][0]);
if(ceil(j*sb[i-1]+0*sa[i-1],k)*k<=0*sa[i]+j*sb[i])
upmin(g[i][j][0],ceil(j*sb[i-1]+0*sa[i-1],k));
}
for(int l=1;l<j;l++)
if(f[i-1][j-l][0]!=inf&&g[i][l][0]!=inf)
{
ll m=0*sa[i]+l*sb[i]-k*g[i][l][0];
ll x=ceil(max(0ll,m+(j-l)*b[i]-c[i]),k);
if(__int128(k)*x>m)
continue;
upmin(f[i][j][0],g[i][l][0]+x+f[i-1][j-l][0]);
if(ceil((j-l)*sb[i-1],k)*k<=m-x*k+(j-l)*sb[i])
upmin(g[i][j][0],g[i][l][0]+x+ceil((j-l)*sb[i-1],k));
}
}
for(int i=1;i<=n;i++)
for(int j=0;j<=t;j++)
{
f[i][j][1]=g[i][j][1]=inf;
if(1*a[i]+j*b[i]<=c[i]&&f[i-1][j][1]!=inf)
{
upmin(f[i][j][1],f[i-1][j][1]);
if(ceil(j*sb[i-1]+1*sa[i-1],k)*k<=1*sa[i]+j*sb[i])
upmin(g[i][j][1],ceil(j*sb[i-1]+1*sa[i-1],k));
}
for(int l=0;l<j;l++)
if(f[i-1][j-l][0]!=inf&&g[i][l][1]!=inf)
{
ll m=1*sa[i]+l*sb[i]-k*g[i][l][1];
ll x=ceil(max(0ll,m+(j-l)*b[i]-c[i]),k);
if(__int128(k)*x>m)
continue;
upmin(f[i][j][1],g[i][l][1]+x+f[i-1][j-l][0]);
if(ceil((j-l)*sb[i-1],k)*k<=m-x*k+(j-l)*sb[i])
upmin(g[i][j][1],g[i][l][1]+x+ceil((j-l)*sb[i-1],k));
}
}
ll ans=f[n][t][1];
printf("%lld\n",ans);
return 0;
}

【集训队作业2018】【XSY3372】取石子 DP的更多相关文章

  1. [集训队作业2018]蜀道难——TopTree+贪心+树链剖分+链分治+树形DP

    题目链接: [集训队作业2018]蜀道难 题目大意:给出一棵$n$个节点的树,要求给每个点赋一个$1\sim n$之内的权值使所有点的权值是$1\sim n$的一个排列,定义一条边的权值为两端点权值差 ...

  2. [UOJ422][集训队作业2018]小Z的礼物——轮廓线DP+min-max容斥

    题目链接: [集训队作业2018]小Z的礼物 题目要求的就是最后一个喜欢的物品的期望得到时间. 根据$min-max$容斥可以知道$E(max(S))=\sum\limits_{T\subseteq ...

  3. 【UOJ#422】【集训队作业2018】小Z的礼物(min-max容斥,轮廓线dp)

    [UOJ#422][集训队作业2018]小Z的礼物(min-max容斥,轮廓线dp) 题面 UOJ 题解 毒瘤xzy,怎么能搬这种题当做WC模拟题QwQ 一开始开错题了,根本就不会做. 后来发现是每次 ...

  4. 【UOJ#450】【集训队作业2018】复读机(生成函数,单位根反演)

    [UOJ#450][集训队作业2018]复读机(生成函数,单位根反演) 题面 UOJ 题解 似乎是\(\mbox{Anson}\)爷的题. \(d=1\)的时候,随便怎么都行,答案就是\(k^n\). ...

  5. UOJ#418. 【集训队作业2018】三角形

    #418. [集训队作业2018]三角形 和三角形没有关系 只要知道儿子放置的顺序,就可以直接模拟了 记录历史最大值 用一个pair(a,b):之后加上a个,期间最大值为增加b个 合并? A1+A2= ...

  6. UOJ#422. 【集训队作业2018】小Z的礼物

    #422. [集训队作业2018]小Z的礼物 min-max容斥 转化为每个集合最早被染色的期望时间 如果有x个选择可以染色,那么期望时间就是((n-1)*m+(m-1)*n))/x 但是x会变,中途 ...

  7. UOJ#428. 【集训队作业2018】普通的计数题

    #428. [集训队作业2018]普通的计数题 模型转化好题 所以变成统计有标号合法的树的个数. 合法限制: 1.根标号比子树都大 2.如果儿子全是叶子,数量B中有 3.如果存在一个儿子不是叶子,数量 ...

  8. UOJ #449. 【集训队作业2018】喂鸽子

    UOJ #449. [集训队作业2018]喂鸽子 小Z是养鸽子的人.一天,小Z给鸽子们喂玉米吃.一共有n只鸽子,小Z每秒会等概率选择一只鸽子并给他一粒玉米.一只鸽子饱了当且仅当它吃了的玉米粒数量\(≥ ...

  9. 2019.2.25 模拟赛T1【集训队作业2018】小Z的礼物

    T1: [集训队作业2018]小Z的礼物 我们发现我们要求的是覆盖所有集合里的元素的期望时间. 设\(t_{i,j}\)表示第一次覆盖第i行第j列的格子的时间,我们要求的是\(max\{ALL\}\) ...

  10. uoj450 【集训队作业2018】复读机(生成函数,单位根反演)

    uoj450 [集训队作业2018]复读机(生成函数,单位根反演) uoj 题解时间 首先直接搞出单个复读机的生成函数 $ \sum\limits_{ i = 0 }^{ k } [ d | i ] ...

随机推荐

  1. vue2.0 实现全选和全不选

    实现思路: 1. v-model 一个收集所有input(除全选框外)数组checkModel ,vue会动态将其checked为true的input的value值存入数组checkModel里 2 ...

  2. Docker 使用 Dockerfile 构建自己的镜像

    可以使用Dockerfile的配置文件方式进行构建自己的镜像 下面利用docker构建一个Caddy web服务器 构建脚本 Dockerfile有自己的命令,下面使用了一些比较常用的命令,更多的Do ...

  3. Redmine入门-安装

    Redmine提供了两种方式安装,如果仅仅只是使用Redmine,建议采用一键安装的方式,快捷方便.如果需要做二次开发或者更多的个性化处理,可以采用源码安装方式,下面分别介绍两种安装方式. ----- ...

  4. FIFO深度计算

    本文设计思想采用明德扬至简设计法.在使用FPGA设计系统时,常需要利用FIFO进行数据缓存保证数据不丢失,因此计算FIFO深度是至关重要的.FIFO的深度主要取决于“最恶劣”的情况,以下对于两种最常见 ...

  5. 记录Nginx作为静态资源web服务场景配置

    server { listen   80; server_name    localhost; sendfile    on; access_log    /var/log/nginx/host.ac ...

  6. java网络爬虫基础学习(三)

    尝试直接请求URL获取资源 豆瓣电影 https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort= ...

  7. Doctype知识点总结

    DOCTYPE是document type (文档类型) 的缩写.<!DOCTYPE >声明位于文档的最前面,处于标签之前,它不是html标签.主要作用是告诉浏览器的解析器使用哪种HTML ...

  8. Dora.Interception,为.NET Core度身打造的AOP框架 [1]:更加简练的编程体验

    很久之前开发了一个名为Dora.Interception的开源AOP框架(github地址:https://github.com/jiangjinnan/Dora,如果你觉得这个这框架还有那么一点价值 ...

  9. remote: Permission to user_name/Code.git denied to other_user_name. fatal: unable to access 'https://github.com/user_name/Code.git/': The requested URL returned error: 403

    Error msg: $ git push remote: Permission to xxx/Code.git denied to xxxxxx. fatal: unable to access ' ...

  10. Γ(a) 的两种方差与均值

    所以 这里是满足 Be(x+1,n-x+1),如果是要服从Be(a,b) 相应的后验概率