AtCoder Regular Contest 128(补题)

D - Neq Neq

链接: link.

题意:

给一个长度为 N N N的序列, A i , A 2 . . . . . A n A_i,A_2.....A_n Ai,A2.....An,现在可以删除一些数,删除的原则是,三个相邻的数, A x , A y , A z A_x,A_y,A_z Ax,Ay,Az,可以删除 A y A_y Ay,问能形成多少种序列,序列不同,只要里面有一个数的下标不同,就算序列不同。

思路:

f ( i ) f(i) f(i)代表以第 i i i个数为结尾的序列方案数
那么此时想要求出 f ( i ) f(i) f(i),序列无论是何种情况,都可以继承 i − 1 i-1 i1的方案序列,就相当于以第 i − 1 i-1 i1个数为结尾形成的序列之后再加一个数 A i A_i Ai
如果此时 A i = A i − 2 且 A i ! = A i − 1 A_i=A_{i-2}且A_i!=A_{i-1} Ai=Ai2Ai!=Ai1,此时,可以把 A i − 1 A_{i-1} Ai1这个数删了,然后以 A i − 2 A_{i-2} Ai2为结尾的序列,在后面放 A i A_i Ai,所以就相当于 A i A_i Ai继承了 A i − 2 A_{i-2} Ai2的方案序列
此时如果 i i i之前出现了,一段序列的数是不相同的情况,即 A j 和 A j − 1 A_j和A_{j-1} AjAj1不相同的一段连续序列,那么此时 f ( i ) f(i) f(i)此时就可以继承这段的序列方案,就例如 a a a a c d e a b a aaaacdeaba aaaacdeaba,[1234…8 9 10]因为 ~~里面的数都不相同,pos2的位置定在3,pos1定在8,就可以在先删除b再删除a后,形成 a a a a c d e a aaaacdea aaaacdea,继承以 e e e为结尾的序列方案数,再删除e,然后再以 d d d为结尾的序列,一直到a为止。所以,就需要 p o s 1 和 p o s 2 pos1和pos2 pos1pos2,pos2标记最近的出现 A j 和 A j − 1 A_j和A_{j-1} AjAj1相同的位置,然后pos1,记录的位置是最近出现 A j 和 A j − 2 A_j和A_{j-2} AjAj2不相同的位置,因为只有当 A j 和 A j − 2 A_j和A_{j-2} AjAj2 A j 和 A j − 1 A_j和A_{j-1} AjAj1,这样就可以删除 A j − 1 和 A j − 2 A_{j-1}和A_{j-2} Aj1Aj2这两个数了

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
const int mod = 998244353;

int a[N];
int dp[N], sum[N];
int n;

signed main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    dp[1] = 1, sum[1] = 1;
    int pos1 = 0, pos2 = 0;
    for (int i = 2; i <= n; i++) {
        (dp[i] += dp[i - 1]) %= mod;
        if (a[i] == a[i - 2] && a[i] != a[i - 1] && i >= 3) {
            (dp[i] += dp[i - 2]) %= mod;
        }
        if (a[i] == a[i - 1]) {
            pos2 = i - 1;
        }
        if (a[i] != a[i - 2] && i >= 3) {
            pos1 = i - 2;
        }
        if (pos1 > pos2) {
            (dp[i] += (sum[pos1] - sum[pos2] + mod) % mod) %= mod;
        }
        sum[i] = (sum[i - 1] + dp[i]) % mod;
    }
    cout << dp[n] << endl;
}

C - Max Dot

链接: link.

题意:

N , M , S N,M,S N,M,S三个数字, A 1 . . . . A n A_1....A_n A1....An序列长度为n,让做一个 B 1 . . . B n B_1...B_n B1...Bn序列,序列 B 1 . . . B n B_1...B_n B1...Bn,满足两个条件 B 1 < = B 2 < = B 3 . . . . < = B n < = M B_1<=B_2<=B_3....<=B_n<=M B1<=B2<=B3....<=Bn<=M ∑ i = 1 n B i \sum \limits_{i=1} ^{n}Bi i=1nBi = = =S,现在让你使 ∑ i = 1 n A i ∗ B i \sum \limits_{i=1} ^{n}A_i*B_i i=1nAiBi最大,然后输出最大值。

思路:

在没有所有数 < = M <=M <=M这个条件下,可以引入一个后缀性价比的概念,即后缀和 s u f ( i ) suf(i) suf(i),然后求性价比最高的一个后缀和, s u f ( i ) l e n \frac{suf(i)}{len} lensuf(i)最大,假设长度为 l e n len len,从 n n n往前一直到了 n − l e n + 1 n-len+1 nlen+1为止,在 B i B_i Bi数组中对应位置赋值为 S l e n \frac{S}{len} lenS即可,这样总和就是 S S S了,可以在官方题解找到关于这个解法的简单证明。
此时有了M的限制,那么把算出来的值 S l e n \frac{S}{len} lenS M M M进行比较,如果 S l e n \frac{S}{len} lenS>= M M M,此时所有值对应位置都赋值为M即可,即满足了<=M的要求,且通过填充前面的数来使总和达到S
如果 S l e n \frac{S}{len} lenS< M M M,那么所有值都赋值为 S l e n \frac{S}{len} lenS即可。

#include <bits/stdc++.h>
using namespace std;
const int N = 5050;
const double eps = 1e-8;
#define int long long
int a[N], suf[N];
int n, m, s;
double b[N];
double res;
int mx;
signed main() {
    cin >> n >> m >> s;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int j = n; j && s >= eps;) {
        mx = 0;
        suf[j + 1] = 0;
        for (int i = j; i; i--) {
            suf[i] = suf[i + 1] + a[i];
        }

        for (int i = j; i; i--) {
            b[i] = 1.0 * suf[i] / (j - i + 1);
            if (b[i] > b[mx]) {
                mx = i;
            }
        }

        double temp = min(1.0 * (j - mx + 1) * m, (double)s);
        res += temp * b[mx];
        s -= temp;
        j = mx - 1;
    }
    printf("%.10lf\n", res);
    return 0;
}

To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值