jzoj 2867. 【集训队互测 2012】Contra

Description

偶然间,chnlich 发现了他小时候玩过的一个游戏“魂斗罗”,于是决定怀旧。但是这是一个奇怪的魂斗罗 MOD。
有 N 个关卡,初始有 Q 条命。
每通过一个关卡,会得到 u 分和1条命,生命上限为 Q。其中 u=min(最近一次连续通过的关数,R)。
若没有通过这个关卡,将会失去1条命,并进入下一个关卡。
当没有生命或没有未挑战过的关卡时,游戏结束,得到的分数为每关得到的分数的总和。
由于 chnlich 好久不玩这个游戏了,每条命通过每个关卡的概率均为p(0<=p<=1),原先 chnlich 的最高分纪录是 S。
现在 chnlich 想要知道,当 p 至少为多少时,chnlich 期望获得的总分数能够超过原先的最高分。

Input

输入共一行,分别表示整数 N,整数 R,整数 Q,原先的最高分整数 S。

Output

输出共一行,若不存在这样的 p,输出"Impossible."(不包含引号),否则输出 p(保留6位小数)。

Sample Input

【样例输入一】
4 2 1 5

【样例输入二】
12 3 2 12

Sample Output

【样例输出一】
0.880606

【样例输出二】
0.687201

Data Constraint

【数据说明】
对于\(20%\)的数据,\(N<=15\)
对于\(50%\)的数据,\(N<=10000\)
对于\(100%\)的数据,\(N<=10^8,1<=R<=20,1<=Q<=5\),保证\(S\)是一个可能出现的分数。

【补充说明】
例如,当 N=12,R=3,Q=2时
第一关:未通过 u=0 获得分数0 总分为0 剩余生命1
第二关:通过 获得分数1 总分为1 剩余生命2
第三关:通过 获得分数2 总分为3 剩余生命2
第四关:通过 获得分数3 总分为6 剩余生命2
第五关:通过 获得分数3 总分为9 剩余生命2
第六关:未通过 获得分数0 总分为9 剩余生命1
第七关:通过 获得分数1 总分为10 剩余生命2
第八关:未通过 获得分数0 总分为10 剩余生命1
第九关:未通过 获得分数0 总分为10 剩余生命0
游戏结束 总分为10
这是 chnlich 游戏的一种可能性

Solution

看完题后想到了\(概率DP\),状态转移方程很容易得出,但是不好求答案。最后弃了。
正解是\(二分答案+概率DP+矩乘优化\)。
我们先二分一个\(p\),然后对于这个\(p\)做\(DP\)。
设\(f[i][j][k]\)表示玩到第\(i\)关,当前\(u\)为\(j\),\(k\)连胜的概率。
那么我们很容易的到转移方程(胜和负)
而答案就是所有的\(f[i][j][k]*p*min(j+1,r)\)的和,比较S再二分即可。
这样\(O(log(1000000)*n*r*q)\),50分到手。
然后我们考虑矩乘优化。
我们将\(p*q\)的数组编号,\(1\)~\(len-1\)
我们设f[i][j]表示从i状态转移到j状态的概率。
而len的那一列表示答案。先求出初始矩阵,然后转移即可。(详见标)
记得注意精度问题!!!

Code

#include <cstdio>
#include <cstring>
#define db double
#define mem(a, x) memset(a, x, sizeof a)
#define fo(x, a, b) for (int x = a; x <= b; x++)
using namespace std;
int n, R, Q, S, len = 0, num[21][6];
db l = 0.0, r = 1.0, mid;

struct matrix
{
    db a[110][110];
    matrix operator *(const matrix &b)
    {
        matrix c;
        mem(c.a, 0);
        fo(k, 1, len)
            fo(i, 1, len)
            {
                if (a[i][k] < 0.00000000001) continue;
                fo(j, 1, len)
                    c.a[i][j] += a[i][k] * b.a[k][j];
            }
        return c;
    }
}a, s, c;

inline int read()
{
    int x = 0; char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x;
}

inline int min(int x, int y) {return x < y ? x : y;}

bool check(db p)
{
    mem(a.a, 0);
    a.a[len][len] = 1.0;
    fo(j, 0, R)
        fo(k, 1, Q)
        {
            a.a[num[j][k]][len] += j * p;
            a.a[num[j][k]][num[min(j + 1, R)][min(k + 1, Q)]] += p;
            a.a[num[j][k]][num[1][k - 1]] += 1 - p;
        }
//  printf("%d\n", len);
//  fo(i, 1, len)
//  {
//      fo(j, 1, len)
//          printf("%.2lf ", a.a[i][j]);
//      puts("");
//  }
    int y = n;
    mem(s.a, 0);
    fo(i, 1, len) s.a[i][i] = 1;
    while (y)
    {
        if (y & 1) s = s * a;
        a = a * a; y >>= 1;
    }
    return s.a[num[1][Q]][len] > S;
}

int main()
{
    freopen("contra.in", "r", stdin);
    freopen("contra.out", "w", stdout);
    n = read(), R = read(), Q = read(), S = read();
    fo(j, 0, R)
        fo(k, 1, Q)
            num[j][k] = ++len;
    ++len;
    if (! check(1.0)) {printf("Impossible."); return 0;}
    while (l + 0.000000001 < r)
    {
        mid = (l + r) / 2.0;
        if (check(mid)) r = mid;
        else l = mid;
    }
    printf("%.6lf", r);
    return 0;
}

原文地址:https://www.cnblogs.com/jz929/p/11291416.html

时间: 2024-07-31 04:26:31

jzoj 2867. 【集训队互测 2012】Contra的相关文章

jzoj 2866. 【集训队互测 2012】Bomb

Description 给你\(n\)个点,坐标分别为\((xi,yi)\).从中取出三个点,使得其两两间曼哈顿距离和最大和最小,求最大值和最小值. 对于 100% 的数据, N<=100000 , 0<=Xi,Yi<=10^8 Solution 看完题目后感觉要分类讨论,思考1h后果断暴力O(n^3). 但是判断了一下n<=500才跑暴力,得了30分.(?10^9过4s?不判50分) 其实可以O(n^2)暴力的.(听说O(n^2)+优化 = 100) 对于第一个最大值,我们可以\

[JZOJ2866] 【集训队互测 2012】Bomb

题目 题目大意 给你一个有\(n\)个点的平面. 选择三个点,求两两之间曼哈顿距离和的最大值和最小值. 思考历程&正解 比赛的时候没有想太多,但感觉似乎比较水-- 首先有个很显然的性质,答案为这三个点的最大最小横坐标之差和最大最小纵坐标之差的和. 可以把它看成矩形的周长,容易发现矩形至少一个顶点是三个点之一. 后来才发现水的是求最大值,而不是求最小值. 比赛之后开始和WMY刚-- 最大值是很好求的.我一开始打了个线段树来求,后来发现根本不用-- 求出所有点的\(Xmin,Xmax,Ymin,Ym

[集训队互测2012]calc——DP

题面 Bzoj2655 解析 可以强制让$a$数列递增,最后乘以$n!$ 有一个显然的$dp$,$f[i][j]$表示填前$i$个位置,且填的数最大不超过$j$的序列权值和,易有:$f[i][j] = f[i][j-1] + f[i-1][j-1] * j$ $O(AN)$的$dp$显然会$T$ 设$f[i][j]$是关于$j$的$g(i)$次多项式,$g(0)=0$ 注意到$f[i][j] - f[i][j-1]$就是关于$j$的$g(i)-1$次多项式,将$dp$方程移项:$f[i][j]

UOJ#191. 【集训队互测2016】Unknown

题意:维护一个数列,每个元素是个二维向量,每次可以在后面加一个元素或者删除一个元素.给定P(x,y),询问对于[l,r]区间内的元素$S_i$,$S_i \times P$的最大值是多少. 首先简单地推出类似斜率优化的式子,那么我们需要在凸包上二分. 学习了一下这份代码http://uoj.ac/submission/69959 使用线段树按下标维护凸包.那么这里有一个问题,如果按照传统的写法,合并一次的复杂度是与$O(区间长度)$的,这样会导致单次插入/删除的时间复杂度变为$O(n)$,是不能

集训队互测2016Unknown(UOJ191)

题目链接 前面部分和lzz的题解是一样的. 首先将输入点(x,y)变为(-y,x)然后,只需找一个向量与(-y,x)的点积最大,即找一个向量在(-y,x)上的投影最长.此时所有的点都是在x轴上方的,容易发现答案一定是在凸包上的,再继续观察,如果有一个点在凸包而不在上凸包上,那么它的右上角及左上角一定有一个点,因此这个点一定不是最优的,所以答案一定在上凸包上,且可以在上凸包上二分. 对于subtask5,使用线段树,每个节点存储这个区间的凸包,合并凸包的话可以将两个凸包上的点归并后线性做凸包. 从

EZ 2018 05 06 NOIP2018 慈溪中学集训队互测(五)

享受爆零的快感 老叶本来是让初三的打的,然后我SB的去凑热闹了 TM的T2写炸了(去你妹的优化),T1连-1的分都忘记判了,T3理所当然的不会 光荣革命啊! T1 思维图论题,CHJ dalao给出了正解但-1输成0了缅怀 而且这题不能用读优玄学 思路也很新奇,先跑一遍MST,判断是否有无解的情况 然后看一下MST中与1相连的边有几条 如果小于k那么我们把所有与1相连的边减上一个值使它们优先被选,然后跑MST 大于k就加上去即可 注意到这个值可以二分,因此不停做MST即可 CODE #inclu

[JZOJ3302] 【集训队互测2013】供电网络

题目 题目大意 给你一个有向图,每个点开始有一定的水量(可能为负数),可以通过边流到其它点. 每条边的流量是有上下界的. 每个点的水量可以增加或减少(从外界补充或泄出到外界),但是需要费用,和增加(减少)流量呈正比例函数关系. 每条边的流量也需要费用,费用和流量呈二次函数关系(常数项为\(0\)). 问将所有水流完的最小花费. 思考历程 这显然是一道上下界最小费用可行流嘛! ==有源有汇的上下界可行流的做法:建立超级源\(ss\)和超级汇\(tt\)(和\(S\).\(T\)),对于\(u\)到

【uoj#94】【集训队互测2015】胡策的统计(集合幂级数)

题目传送门:http://uoj.ac/problem/94 这是一道集合幂级数的入门题目.我们先考虑求出每个点集的连通生成子图个数,记为$g_S$,再记$h_S$为点集$S$的生成子图个数,容易发现,$h_S=2^{size_S}$,其中$size_S$为点集$S$的极大生成子图内的边数.特殊的,$f_{\o}=g_{\o}=0$. 定义集合幂级数的乘法为子集卷积,考虑集合幂级数$h$和$g$的关系,我们可以得到 $$h=1+\sum_{k \geq 1}\frac{g^k}{k!}=1+e^

小组互测评论

时间:2014年5月9号 互测小组:本组与刘铸辉(组长)组 第一版本互测初步评论: 杨波:整体界面很好,要实现的基本功能实现了: 崔海营:日程录入模块实现的很好,调用了系统设计: 周亚豪:每周日历表这个模块导入效果不错,整体界面良好: 蔡容玉:日程录入模块实现了,但是好像录入了后在每天时间表的事件中没有及时更新: 闵芮:界面较简便,功能不错:期待后续版本: 张丹丹:软件界面不美观,手机屏幕利用率不高,但总体功能比较完善,对学生有一定的实用性. 高琪:界面清晰,功能明确,但是希望界面更好看: