zoukankan      html  css  js  c++  java
  • Codeforces 958C3

    C3 - Encryption (hard)

    思路:

    记sum[i]表示0 - i 的和对 p 取模的值。

    1.如果k * p > n,那么与C2的做法一致,O(k*p*n)复杂度低于1e8。

    2.如果k * p <= n

    那么根据抽屉原理,必有k个sum[i]相同,

    那么任意取k - 1个相同的 sum[i],记它们的下标为 l1,l2,......,lk-1 ,那么显然区间[li + 1, li+1](1<=i<k-1)的贡献为0

    有贡献的区间只有[1,l1]和[lk-1 + 1,n]由于两个区间的贡献加起来小于2 * (p - 1) ,所以最后的答案要么为 sum[n],要么为 sum[n] + p

    那么怎么判断是前者还是后者呢?

    只要判断在sum中能不能找到一个以sum[n]结尾的长度为k的非严格上升子序列就可以了

    如果能找到就是sum[n],否则就是 sum[n] + p

    LIS的复杂度O(nlogn)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    #define pb push_back
    #define mem(a, b) memset(a, b, sizeof(a))
    
    const int N = 5e5 + 5;
    const int INF = 0x3f3f3f3f;
    int a[N];
    int dp[105][105];
    int _dp[N];
    int main() {
        int n, k, p;
        scanf("%d%d%d", &n, &k, &p);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]),a[i] += a[i-1], a[i] %= p;
        if (k * p > n) {
            mem(dp, INF);
            dp[0][0] = 0;
            for (int i = 1; i <= n; i++) {
                for (int j = k; j >= 1; j--) {
                    for (int l = 0; l < p; l++){
                        dp[a[i]][j] = min(dp[a[i]][j], dp[l][j-1] + ((a[i] - l)%p+p)%p);
                    }
                }
            }
            printf("%d
    ", dp[a[n]][k]);
        }
        else {
            mem(_dp, INF);
            for (int i = 1; i <= n - 1; i++) {
                *upper_bound(_dp + 1, _dp + n, a[i]) = a[i];
            }
            if(_dp[k - 1] <= a[n]) printf("%d
    ", a[n]);
            else printf("%d
    ", a[n] + p);
        }
        return 0;
    }
  • 相关阅读:
    多线程
    关于并发和并行
    文件系统
    java.util.Arrays类
    程序管理与SElinux
    用户和用户组管理总结
    useradd和adduser
    打印可见字符
    cmd中控制某个命令执行多少次
    keras模块之-优化器(optimizers)--笔记
  • 原文地址:https://www.cnblogs.com/widsom/p/8863005.html
Copyright © 2011-2022 走看看