首页 > 其他分享 >P5665 [CSP-S2019] 划分 做题记录

P5665 [CSP-S2019] 划分 做题记录

时间:2023-09-03 14:34:46浏览次数:39  
标签:小明 P5665 lastlen back iasum leq S2019 CSP dp

题目传送门

题目描述

2048 年,第三十届 CSP 认证的考场上,作为选手的小明打开了第一题。这个题的样例有 \(n\) 组数据,数据从 \(1 \sim n\) 编号,\(i\) 号数据的规模为 \(a_i\)。

小明对该题设计出了一个暴力程序,对于一组规模为 \(u\) 的数据,该程序的运行时间为 \(u^2\)。然而这个程序运行完一组规模为 \(u\) 的数据之后,它将在任何一组规模小于 \(u\) 的数据上运行错误。样例中的 \(a_i\) 不一定递增,但小明又想在不修改程序的情况下正确运行样例,于是小明决定使用一种非常原始的解决方案:将所有数据划分成若干个数据段,段内数据编号连续,接着将同一段内的数据合并成新数据,其规模等于段内原数据的规模之和,小明将让新数据的规模能够递增。

也就是说,小明需要找到一些分界点 \(1 \leq k_1 \lt k_2 \lt \cdots \lt k_p \lt n\),使得

\[\sum_{i=1}^{k_1} a_i \leq \sum_{i=k_1+1}^{k_2} a_i \leq \cdots \leq \sum_{i=k_p+1}^{n} a_i \]

注意 \(p\) 可以为 \(0\) 且此时 \(k_0 = 0\),也就是小明可以将所有数据合并在一起运行。

小明希望他的程序在正确运行样例情况下,运行时间也能尽量小,也就是最小化

\[(\sum_{i=1}^{k_1} a_i)^2 + (\sum_{i=k_1+1}^{k_2} a_i)^2 + \cdots + (\sum_{i=k_p+1}^{n} a_i)^2 \]

小明觉得这个问题非常有趣,并向你请教:给定 \(n\) 和 \(a_i\),请你求出最优划分方案下,小明的程序的最小运行时间。

数据范围

所有测试点满足:\(type \in \{0,1\}\),\(2 \leq n \leq 4 \times 10^7\),\(1 \leq a_i \leq 10^9\),\(1 \leq m \leq 10^5\),\(1 \leq l_i \leq r_i \leq 10^9\),\(0 \leq x,y,z,b_1,b_2 \lt 2^{30}\)。

思路

不难想到 \(\Theta (n^3)\) 的 dp:对每个点 \(i\),记录 \(d_{i,j}\) 表示以 \(i\) 结尾,最长一段为 \((j, i]\) 的费用。\(j \in [-1, i)\)。转移时枚举点 \(i\),转移到点 \(i\) 的 \(j\),转移到 \(j\) 的点 \(k\)。

// 完整代码见末尾,只有 namespace m 的差异
namespace O_n3{ // }{{{
ll dpp[405][405];
ll from[405][405];
auto &dp(int x, int y){ return dpp[x+1][y+1]; }
auto &fr(int x, int y){ return from[x+1][y+1]; }
void work(){
    Interacter::init();
    assert(in <= 400);
    UP(i, 0, in) UP(j, -1, i){
        dp(i, j) = j==-1 ? iasum[i]*iasum[i] : INF;
        ll len = (iasum[i]-iasum[j])*(iasum[i]-iasum[j]);
        UP(k, -1, j){
            if(iasum[j]-iasum[k] <= iasum[i]-iasum[j]) {
                if(dp(j, k)+len < dp(i, j)){
                    dp(i, j) = dp(j, k) + len;
                    fr(i, j) = k;
                }
            }
        }
    }
    ll ans = INF;
    ll k = 0;
    UP(i, -1, in-1){
        if(dp(in-1, i) < ans){
            ans = (dp(in-1, i));
            k = i;
        }
    }
    if(ans/10000000000000000000LLU) cout << (unsigned long long)(ans/10000000000000000000LLU);
    cout << (unsigned long long)(ans%10000000000000000000LLU) << '\n';
}
} // {}}}

但 \(n \le 4 \times 10^7\) 的数据范围显然不允许我们这么做,需要 \(O(n \log n)\) 的做法。下面提供一个 \(\Theta(n)\) 的做法。

看题解后发现一个结论:在最优划分里,最后一段一定无法缩短。

反证:假设可以缩短,则踢出去的这一部分无论是给别人还是单独成段都比在最后一段优,不满足最优划分。

于是,对于每个点我们只需记录最短一段的长度及费用即可。

发现答案具有单调性,考虑单调队列。

dp 过程中,设正在求答案的点为 \(i\),对于队首的点 \(j\),如果它后面的点 \(k\) 可以接在 \(i\) 前面(即使 \(i\) 结尾的划分最长一段更短),则由之前的结论,\(k\) 一定比 \(j\) 不劣,\(j\) 出队。这样一直到一个不能出队的点 \(l\),它就是可以转移的 \(i\) 的最优答案。

然而,这样还需保证队列中 \(l\) 以后的点全都不能转移到 \(i\)。

在加入点时,考虑何时队尾的点会不如当前加入的点:\(back.maxlen - ( \sum_{i \in (last.pos, now.pos]} a_i) \ge now.maxlen\)

也就是 dp.back().lastlen - (iasum[i] - iasum[dp.back().pos]) >= topush.lastlen

不断弹掉这些点即可保证单调性。

每个点至多进队出队一次,总时间是 \(\Theta(n)\) 的。

#include <cassert>
#include <iostream>
#include <list>
#define UP(i,s,e) for(auto i=s; i<e; ++i)
using std::cin; using std::cout;
using ll = long long;
using ull = unsigned long long;
using i128 = __int128;
constexpr int N = 4e7;
namespace m{ // }{{{
int in; ll iasum_[N+1], *const iasum=iasum_+1;
} // {}}}
namespace Interacter{ // }{{{
using m::in; using m::iasum;
int typ, ix, iy, iz, ib1, ib2, nb, im; // nb = new b
int *ib;
void init(){
    cin >> in >> typ;
    if(typ == 0){
        UP(i, 0, in){
            cin >> iasum[i];
        }
    } else {
        cin >> ix >> iy >> iz >> ib1 >> ib2 >> im;
        ib = new int[in];
        ib[0] = ib1; ib[1] = ib2;
        UP(i, 2, in){
            ib[i] = (
                    + (unsigned)ib[i-2]*(unsigned)iy 
                    + (unsigned)ib[i-1]*(unsigned)ix 
                    + (unsigned)iz
                    ) & 0x3fffffff;
        }
        int ip, il, ir, lastp=0;
        UP(i, 0, im){
            cin >> ip >> il >> ir;
            UP(j, lastp+1, ip+1){
                iasum[j-1] = ib[j-1]%(ir-il+1)+il;
            }
            lastp = ip;
        }
        delete[] ib;
    }
    UP(i, 1, in){
        iasum[i] += iasum[i-1];
    }
}
} // {}}}
namespace m{ // }{{{
extern int in;
extern ll *const iasum;
struct Point{
    int pos; 
    ll lastlen;
    i128 val; // dp val
};
std::list<Point> dp;
void work(){
    Interacter::init();
    dp.push_back({-1, 0, 0});
    UP(i, 0, in){
        auto j = dp.begin();
        while(j != --dp.end()){
            auto k = j; k++;
            if(k->lastlen <= iasum[i]-iasum[k->pos]){
                j = dp.erase(j);
            } else break;
        }
        assert(j->lastlen <= iasum[i]-iasum[j->pos]);
        Point topush = {i, iasum[i]-iasum[j->pos], j->val};
        topush.val += (topush.lastlen) * (i128)(topush.lastlen);
        while(!dp.empty()){
            if(dp.back().lastlen - (iasum[i] - iasum[dp.back().pos]) >= topush.lastlen) dp.pop_back();
            //else if(dp.back().val >= topush.val) dp.pop_back();
            else break;
        }
        dp.push_back(topush);
    }
    i128 ans = dp.back().val;
    // Python 测试了一下 1e19 会掉精度
    if(ans / 10000000000000000000LLU) cout << (ull)(ans/10000000000000000000LLU);
    cout << (ull)(ans%10000000000000000000LLU) << '\n';
}
} // {}}}
int main(){std::ios::sync_with_stdio(0); cin.tie(0); m::work(); return 0;}

标签:小明,P5665,lastlen,back,iasum,leq,S2019,CSP,dp
From: https://www.cnblogs.com/x383494/p/17674936.html

相关文章

  • P8819 [CSP-S 2022] 星战 做题记录
    不可以,总司令。题目传送门思路首先,当图中每个点出度为\(1\)时,从任一点出发必定会进入环。证明:假设有一点不符合,则沿着它的出边一直走会到一个出度为\(0\)的「终点」,与每个点出度为\(1\)矛盾。想通了这点,这题就不难了。发现出度要\(O(n)\)维护,入度可以\(O(1)\)维护......
  • 【游记】CSP2023赛前集训游记
    9.1赛前集训的前一天。学校报道的日子,大半天都在yzsy上课。晚上回来没有颓废(很难得啊),把线性基学了一下,然后就开始补数学,从\(9\)点补到\(10\)点。然后只写了几章,看来效率不是只有一点点底啊。然后写了一篇脸滚键盘,总结了一下前半段OI生涯所犯的一些错误,汲取一下经验。想......
  • 【考后总结】9 月 CSP-S 模拟赛 1
    9.1CSP模拟32AfterHours-TheWeekndThoughtIalmostdiedinmydreamagain(Baby,almostdied)Fightin'formylife,Icouldn'tbreatheagainI'mfallin'intonew(Oh,oh)Withoutyougoin'smooth(Fallin'in)'Cau......
  • 2023西安/成都/深圳CSPM-3国标项目管理含金量真高,太值了
    CSPM-3中级项目管理专业人员评价,是中国标准化协会(全国项目管理标准化技术委员会秘书处),面向社会开展项目管理专业人员能力的等级证书。旨在构建多层次从业人员培养培训体系,建立健全人才职业能力评价和激励机制的要求,培养我国项目管理领域复合型人才。  【证书含金量】 ·竞聘优先......
  • CSP2022 游记
    2022.6.?报名。2022.7.?缴费,是来捐款的。2022.8.31隔天开学了,很慌,初赛一直都只是看知识点,没有练题。2022.9.1~2022.9.15一直在练题,不过没怎么练阅读程序和完善程序,摆。2022.9.1?我爸又跟我吵了一架,他总是在支持与不支持直接来回跳跃,然后班主任出马,他转变成了支持。2022.9......
  • vs2019-cuda配置入门
    cuda使用如下1、打开VS,新建C++空项目 2、右击源文件->添加->新建项 3、选择CUDAC/C++File,名称位main.cu 4、把下面的示例源码复制到main.cu中#include"cuda_runtime.h"#include"device_launch_parameters.h"#include<stdio.h>/***************************......
  • CSP-J2022初赛易错题解析
    7.假设字母表{a,b,c,d,e}在字符串出现的频率分别为10%,15%,30%,16%,29%。若使用哈夫曼编码方式对字母进行不定长的二进制编码,字母d的编码长度()位。A.1  B.2 C.2或3  D.3正解:画出哈夫曼树即可9.考虑由N个顶点构成的有向连通图,采用邻接矩阵的数据结构表示时,该矩阵中至......
  • CSP-J2020初赛易错题解析
    一.5. 正解:冒泡排序最少比较n-1次,即单调上升序列 10.5 个小朋友并排站成一列,其中有两个小朋友是双胞胎,如果要求这两个双胞胎必须相邻,则有()种不同排列方法?A.24 B.36 C.72 D.48错误原因:忘记乘上A(2,2)了正解:捆绑法,A(4,4)*A(2,2)=48 15.有五副不同颜色的手套(......
  • CSP-J2021初赛易错题解析
    12.由 1,1,2,2,3 这五个数字组成不同的三位数有()种。A.18 B.15 C.12 D.24正解:枚举法,枚举即可,共18种 15.有四个人要从A点坐一条船过河到B点,船一开始在A点。该船一次最多可坐两个人。已知这四个人中每个人独自坐船的过河时间分别为 1,2,4,8,且两个人坐船的......
  • 【题解】 P7077 [CSP-S2020] 函数调用(拓扑排序)
    题意题目给定了一个长度为\(n\)序列\(a\)与\(m\)个操作,操作一共有3种:1.给定\(x,y\),使\(a_x\)增加\(y\)。2.给定\(x\),使\(a\)中所有数全部乘上\(x\)。3.给出k个数\(c_1,c_2,...,c_k\),表示这个操作的任务是按照先后顺序执行编号为\(c_1,c_2,...,c_k\)的\(k\)的操作。最后,题目相......