Codeforces Round #518 (Div. 1) Computer Game 倍增+矩阵快速幂

接近于死亡的选手没有水平更博客,所以现在每五个月更一篇。

这道题呢,首先如果已经有权限升级了,那么后面肯定全部选的是 \(p_ib_i\) 最高的。

设这个值为 \(M=\max \limits_i p_ib_i\)。

主要的问题在于前面怎么选。

假设剩下的时间还有 \(t\) 秒。那么我们很容易得到一个这样的式子。
\[
dp[t+1] = \max_i \{p_i \cdot (a_i+tM) + (1-p_i) \cdot dp[t]\}
\]
把 \(\max\)里面的内容整理一下。
\[
dp[t+1] = dp[t] + \max_i\{p_i\cdot(tM - dp[t]) + a_ip_i\}
\]
如果 \(i\) 确定了,那么 \(\max\) 里面的东西就是一个关于 \(tM-dp[t]\) 的一次函数。

而把一堆一次函数取 \(max\) 显然可以通过维护这些一次函数的凸包,然后在凸包上二分 \(tM-dp[t]\) 的位置解决。

但是这样做的时间复杂度为 \(O((n+t)\log n)\)。

可巧我们有一个显然的结论就是 \(dp[t+1]-dp[t] < M\)。

那么就是说 \(tM-dp[t] < (t+1)M -dp[t+1]\)。也就是说,需要在凸包上走的东西是单调递增的。

由于凸包不超过 \(n\) 段,所以可以在每一段上做矩阵快速幂。
\[
\begin{bmatrix}
1-p_i & p_iM & a_ip_i\0 & 1 & 1\0 & 0 & 1
\end{bmatrix}
\cdot
\begin{bmatrix}
dp[t]\t\1
\end{bmatrix}
=
\begin{bmatrix}
dp[t+1]\t+1\1
\end{bmatrix}
\]
直接二分是 \(O(n(\log n + \log^2t))\) 的。可以使用倍增,\(O(n(\log n+\log t))\)。

#include<bits/stdc++.h>
using namespace std;

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I>
inline void read(I &x) {
    int f = 0, c;
    while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    x = c & 15;
    while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    f ? x = -x : 0;
}

const int N = 1e5 + 7;
const int LOG = 36;
const double eps = 1e-13;

int n, nl;
ll t;
double M;
int a[N], b[N], q[N];
double p[N];

inline int dcmp(const double &x) { return fabs(x) < eps ? 0 : (x < 0 ? -1 : 1); }
struct Line {
    double k, b;
    inline bool operator < (const Line &a) const { return dcmp(k - a.k) < 0 || (dcmp(k - a.k) == 0 && b < a.b); }
//  inline bool operator < (const Line &a) const { return b > a.b; }
} l[N];

inline double crs(const Line &l1, const Line &l2) { return (l2.b - l1.b) / (l1.k - l2.k); }

struct Matrix {
    double a[3][3];
    inline Matrix() { memset(a, 0, sizeof(a)); }
    inline Matrix(const double &x) {
        memset(a, 0, sizeof(a));
        a[0][0] = a[1][1] = a[2][2] = x;
    }

    inline void print() {
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j)
                dbg("%.13lf ", a[i][j]);
            puts("");
        }
    }

    inline Matrix operator * (const Matrix &b) {
        Matrix c;
        c.a[0][0] = a[0][0] * b.a[0][0] + a[0][1] * b.a[1][0] + a[0][2] * b.a[2][0];
        c.a[0][1] = a[0][0] * b.a[0][1] + a[0][1] * b.a[1][1] + a[0][2] * b.a[2][1];
        c.a[0][2] = a[0][0] * b.a[0][2] + a[0][1] * b.a[1][2] + a[0][2] * b.a[2][2];
        c.a[1][0] = a[1][0] * b.a[0][0] + a[1][1] * b.a[1][0] + a[1][2] * b.a[2][0];
        c.a[1][1] = a[1][0] * b.a[0][1] + a[1][1] * b.a[1][1] + a[1][2] * b.a[2][1];
        c.a[1][2] = a[1][0] * b.a[0][2] + a[1][1] * b.a[1][2] + a[1][2] * b.a[2][2];
        c.a[2][0] = a[2][0] * b.a[0][0] + a[2][1] * b.a[1][0] + a[2][2] * b.a[2][0];
        c.a[2][1] = a[2][0] * b.a[0][1] + a[2][1] * b.a[1][1] + a[2][2] * b.a[2][1];
        c.a[2][2] = a[2][0] * b.a[0][2] + a[2][1] * b.a[1][2] + a[2][2] * b.a[2][2];
        return c;
    }
} f[LOG];

inline void ycl() {
    for (int i = 1; i <= n; ++i) smax(M, p[i] * b[i]), l[i] = (Line){ p[i], p[i] * a[i] };
    sort(l + 1, l + n + 1);
    for (int i = 1; i <= n; ++i) if (dcmp(l[i].k - l[nl].k)) l[++nl] = l[i]; else l[nl] = l[i];
    n = nl, nl = 1;
    q[1] = 1;
    for (int i = 2; i <= n; ++i) {
//      if (dcmp(crs(l[q[nl]], l[i])) <= 0) continue;
        while (nl > 1 && dcmp(crs(l[q[nl - 1]], l[i]) - crs(l[q[nl - 1]], l[q[nl]])) <= 0) --nl;
        q[++nl] = i;
    }
    for (int i = 1; i <= nl; ++i) l[i] = l[q[i]];//, dbg("Line : %.13lf %.13lf\n", l[i].k, l[i].b);
    n = nl;
//  for (int i = 1; i <= n; ++i) dbg("Line : %.13lf %.13lf\n", l[i].k, l[i].b);
//  dbg("%.13lf %.13lf\n", crs(l[1], l[4]), crs(l[1], l[3]));
}

inline void work() {
    ycl();

    double dp = 0;
    ll tt = 0;
    for (int i = 1; i <= n && tt < t; ++i) {
        double stp;
        if (i < n) stp = crs(l[i], l[i + 1]);
        else stp = 1e20;

        if (tt < t && dcmp(tt * M - dp - stp) >= 0) continue;

        Matrix A, B;
        A.a[0][0] = dp, A.a[1][0] = tt, A.a[2][0] = 1;
        B.a[0][0] = 1 - l[i].k, B.a[0][1] = l[i].k * M, B.a[0][2] = l[i].b;
        B.a[1][1] = 1, B.a[1][2] = 1;
        B.a[2][2] = 1;
        f[0] = B;
        for (int i = 1; i < LOG; ++i) f[i] = f[i - 1] * f[i - 1];

        for (int i = LOG - 1; ~i; --i) if (tt + (1ll << i) < t) {
            Matrix C = f[i] * A;
//          if (i == 1) C.print();
            if (dcmp(C.a[1][0] * M - C.a[0][0] - stp) < 0) A = C, tt += 1ll << i;
        }
        A = f[0] * A, ++tt;
        dp = A.a[0][0];
    }
    printf("%.13lf\n", dp);
}

inline void init() {
    read(n), read(t);
    for (int i = 1; i <= n; ++i) read(a[i]), read(b[i]), scanf("%lf", &p[i]);
}

int main() {
#ifdef hzhkk
    freopen("hkk.in", "r", stdin);
#endif
    init();
    work();
    fclose(stdin), fclose(stdout);
    return 0;
}

原文地址:https://www.cnblogs.com/hankeke/p/CF1067D.html

时间: 2024-08-05 14:30:51

Codeforces Round #518 (Div. 1) Computer Game 倍增+矩阵快速幂的相关文章

【Codeforces Round #518 (Div. 2)】

A:https://www.cnblogs.com/myx12345/p/9847588.html B:https://www.cnblogs.com/myx12345/p/9847590.html C: D: E: F: 原文地址:https://www.cnblogs.com/myx12345/p/9847591.html

Codeforces Round #518 (Div. 2) D(计数DP)

#include<bits/stdc++.h>using namespace std;const long long mod=998244353;int n;int a[100007];long long dp[100007][207][3];//第i位值为j时k是否成立,k=0,i<i-1,k=1,i==i-1,k=2,i>i-1 int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)   

Codeforces 450B div.2 Jzzhu and Sequences 矩阵快速幂or规律

Jzzhu has invented a kind of sequences, they meet the following property: You are given x and y, please calculate fn modulo 1000000007 (109 + 7). Input The first line contains two integers x and y (|x|, |y| ≤ 109). The second line contains a single i

Codeforces 719E [斐波那契区间操作][矩阵快速幂][线段树区间更新]

/* 题意:给定一个长度为n的序列a. 两种操作: 1.给定区间l r 加上某个数x. 2.查询区间l r sigma(fib(ai)) fib代表斐波那契数列. 思路: 1.矩阵操作,由矩阵快速幂求一个fib数根据矩阵的乘法结合率,A*C+B*C=(A+B)*C; 这样可以通过线段树维护某个区间2*1矩阵的和. 2.时限卡的紧...用我的矩阵乘法板子TLE了.所以把板子里边的三重循环改成手工公式... 3.注意(a+b)%mod.这种,改成if(a+b>=mod)a+b-mod这种形式时间几乎

Codeforces Round #480 (Div. 2) C 贪心 D 数字、思维 E 树上倍增

Codeforces Round #480 (Div. 2) C. Posterized 题意: 给出 n 个数,都是区间 [0,255] 内的数,要你把 [0,255] 划分成多个长度 <=k 的不重叠的子区间.每个数必须包含在一个子区间内,且这个数的价值是这个子区间的左端点.要你输出这 n 数的价值,且这 n 个价值字典序要最小. tags: 首先很明显字典序最小,那对于第 i 个数 p[i] 定它的区间时,左端点肯定要尽可能小.所以我们直接枚举区间 [ p[i]-k+1, p[i] ] 定

Codeforces Round #298 (Div. 2) A、B、C题

题目链接:Codeforces Round #298 (Div. 2) A. Exam An exam for n students will take place in a long and narrow room, so the students will sit in a line in some order. The teacher suspects that students with adjacent numbers (i and i + 1) always studied side

Codeforces Round #549 (Div. 1)

今天试图用typora写题解 真开心 参考 你会发现有很多都是参考的..zblzbl Codeforces Round #549 (Div. 1) 最近脑子不行啦 需要cf来缓解一下 A. The Beatles 这道题就是枚举啦 有两种步长 试一下就好了 如果你的步长是x 那么要跳的次数就是距离除以步长 \[ \frac{n * k * x}{gcd(n * k, x)} \div x = \frac{n * k}{gcd(n * k, x)} \] #include <cmath> #in

Codeforces Round #620(Div. 2)

Codeforces Round #620 (Div. 2) 题目链接 https://codeforces.com/contest/1313 A B C题直接跳过. D题: 求最短的上升子序列,我们可以直接假设它是1-n从大到小排列,然后对于每两个大于号之间的小于号再翻转就行. 求最长的就反过来做一遍就行. 代码: #include<bits/stdc++.h> using namespace std; int n,a[300050],ans[300050]; char s[300050];

Codeforces Round #634 (Div. 3) 补题

A. Candies and Two Sisters 签到题,直接输出即可 代码 #include<bits/stdc++.h> #define INF 0x3f3f3f3f typedef long long ll; using namespace std; inline void read(int &p) { p=0;int flag=1;char c=getchar(); while(!isdigit(c)) {if(c=='-') flag=-1;c=getchar();} w