首页 > 其他分享 >P9744 消除序列 题解

P9744 消除序列 题解

时间:2023-10-16 21:35:49浏览次数:41  
标签:P9744 int 题解 preb 枚举 序列 操作 main 我们

本题有多种解法,我这里先讲一个我的考场做法吧。

切入点

我们发现我们至多使用一次操作一,而剩下部分的 \(0\) 肯定是依靠操作二补全,操作三的作用只是用来填补操作一的空白的,所以我们发现我们对一个序列的操作一定是前一段用操作一和操作三,后一段用操作二。

思路1

一开始考虑暴力 \(O(n)\) 枚举其断点,用个前缀和和后缀和优化即可做到 \(O(nq)\),拿到 \(60\) 分的好成绩。

\(60pts\ Code\)

int n,a[N],b[N],c[N],q,m;
ll pre[N],sub[N];
bool p[N];
int main(){
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)b[i]=read();
    for(int i=1;i<=n;i++)c[i]=read();
    q=read();
    while(q--){
        m=read();
        for(int i=1;i<=n;i++)p[i]=0,pre[i]=sub[i]=0;
        for(int i=1;i<=m;i++)p[read()]=1;
        for(int i=1;i<=n;i++)pre[i]=pre[i-1]+(p[i]?c[i]:0);
        for(int i=n;i;--i)sub[i]=sub[i+1]+(p[i]?0:b[i]);
        ll ans=1e18;
        for(int i=0;i<=n;i++)ans=min(ans,pre[i]+a[i]+sub[i+1]);
        printf("%lld\n",ans);
    }
}

发现瓶颈在于枚举的是 \(n\),而其实我们用的前缀和和后缀和在 \(p_i\) 和 \(p_{i+1}\) 之间是不受干扰的,所以考虑如何把复杂度降到 \(O(\sum m)\)。
我们令 \(C_i\) 表示前 \(i\) 个 \(c_{p_i}\) 之和,\(B_i\) 表示除去 \(p_i,p_{i+1},\sim p_{m}\) 的所有 \(b_{j}\) 之和,\(preb_{i}\) 表示 \(b_i\) 的前缀和,那么我们不难发现对于 \(x\in[p_i,p_{i+1})\) 来说,\(ans=min\{ans,C+B-preb_x+a_x\}\),而 \(a_x-preb_x\) 的最小值我们可以用诸如线段树和 st 表的数据结构来维护,这样可以做到 \(O(n\log n)\) 级别的复杂度。
有个细节特别要注意就是我们不选 \(a_i\) 相当于是从 \(p_0\) 开始,所以建 st 表的时候要注意是 \(k\le \log_2 (n+1)\)。

\(Code1\)

const int N=500005;
int n,q,m,p[N],lg[N];
ll a[N],b[N],c[N],preb[N],st[20][N];
int main(){
    n=read();
    for(int i=2;i<=n+1;i++)lg[i]=lg[i>>1]+1;//n+1
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)st[0][i]=a[i]-(preb[i]=preb[i-1]+(b[i]=read()));
    for(int i=1;i<=n;i++)c[i]=read();
    for(int k=1;k<=lg[n+1];k++)//lg[n+1]
        for(int i=0;i+(1<<k)-1<=n;i++)//一定要从0开始!!
            st[k][i]=min(st[k-1][i],st[k-1][i+(1<<k-1)]);
    q=read();
    while(q--){
        m=read();
        ll B=preb[n],C=0,ans=1e18;
        for(int i=1;i<=m;i++)p[i]=read(),B-=b[p[i]];
        p[m+1]=n+1;
        for(int i=0;i<=m;i++){
            //ans=min(ans,(C+=c[p[i]])+a[p[i]]+((B+=b[p[i]])-preb[p[i]]));
            C+=c[p[i]],B+=b[p[i]];
            int l=p[i],r=p[i+1]-1,k=lg[r-l+1];
            ll w=min(st[k][l],st[k][r-(1<<k)+1]);
            ans=min(ans,w+C+B);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

思路2

上面的方法还是比较复杂了,我们考虑不枚举分界点而是考虑用递推优化这一过程。用 \(f_i\) 表示让前 \(i\) 个变 \(0\) 的最小代价,然后同样因为在相邻的 \(p_i\) 之间值是不受影响的,所以我们枚举 \(p_i\),其前面用 \(f_i\) 后面都用 \(b_i\) 算代价,复杂度 \(O(\sum m)\)。
具体细节看代码:

\(Code2\)

const int N=500005;
int n,q,m,p[N];
ll a[N],b[N],c[N],f[N],sub[N],bac[N];
int main(){
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)f[i]=min(f[i-1]+(b[i]=read()),a[i]);
    for(int i=1;i<=n;i++)c[i]=read();
    for(int i=n;i;--i)sub[i]=sub[i+1]+b[i];
    q=read();
    while(q--){
        m=read();
        for(int i=1;i<=m;i++)p[i]=read();
        bac[m+1]=0;
        for(int i=m;i;--i)bac[i]=bac[i+1]+b[p[i]];
        ll ans=1e18,C=0;
        for(int i=1;i<=m;C+=c[p[i]],i++)
            ans=min(ans,f[p[i]-1]+C+sub[p[i]]-bac[i]);
        printf("%lld\n",min(ans,f[n]+C));
    }
    return 0;
}

标签:P9744,int,题解,preb,枚举,序列,操作,main,我们
From: https://www.cnblogs.com/NBest/p/17768398.html

相关文章

  • P9744 「KDOI-06-S」消除序列
    题目传送门这道题在比赛时先花了一个小时理解好题意才打了一个\(70\)分的\(O(n^2)\)暴力。下午刚起床,有点困,还没进入状态,打得挺慢。具体地,会发现操作实际上是在这个长度为\(n\)的序列找一个点\(i\),将\([0,i]\)通过操作\(1\)全变\(0\),设\(x=\displaystyle\sum_{k\in......
  • CEIT 23练习编程题 题解
    本文部分题目提供c/c++两种解法,顺便可以让你们知道c++在面对某些题时的优势部分题目提供多种解法日期格式化C#include<stdio.h>intmain(){intm,d,y;scanf("%d-%d-%d",&m,&d,&y);printf("%04d-%02d-%02d",y,m,d);return0;}02d的含义:当有效数......
  • 【题解】「KDOI-06-S」补题
    「KDOI-06-S」A.「KDOI-06-S」消除序列赛时写了一个\(O(nq)\)的线性DP,喜提60分。注意到如果操作1被使用,则一定只会使用一次,而且在最优策略中一定是第一次使用操作1。则我们可以通过以下方式进行操作,使序列满足条件:首先执行\(a_i\)和\(\sum^{j\lei,i\inP}_{j=......
  • [COCI2015-2016#4] ENDOR 题解
    [COCI2015-2016#4]ENDOR题解首先要发现一个很重要的性质,那就是两只变色龙碰撞后回头,等效于两只变色龙继续往前走,其中向右走的颜色不变,而向左走的要改变颜色。那这样就有一种\(O(n^2)\)的做法:对于向右的变色龙,直接贡献答案;对于向左的变色龙,我们按照碰到的先后顺序枚举它前面......
  • 【题解】AtCoder-ARC167
    AtCoder-ARC167AToastsforBreakfastParty一定不会有空盘,问题转化成\(2m\)个数,其中\(2m-n\)个是\(0\),这样一定是最大值和最小值一起,次大值和次小值一起,以此类推。提交记录:Submission-AtCoderAtCoder-ARC167BProductofDivisors\(A^B=\prod_ip_i^{Bc_i}\),那么答案......
  • CF1119F Niyaz and Small Degrees 题解
    原题翻译首先\(O(n^2\logn)\)的dp是simple的,我们设\(dp_{i,0/1}\)表示以\(i\)为根,\(i\)到\(fa_i\)这条边删/不删的最小权值和。转移是一个非常trick的问题,只需要假设所有都选\(dp_{i,0}\),然后把所有儿子按照\(dp_{v,1}+w(u,v)-dp_{v,0}\)排序,选前\(d......
  • P9745 「KDOI-06-S」树上异或 题解
    P9745「KDOI-06-S」树上异或题解\(x_i=0\)这题一看就不是很可做,先考虑部分分。对于一条链的情况,我们可以枚举上一个断边的位置,然后转移。一看数据范围,估计和值域有关,所以考虑\(x_i=1\)的部分分,如果全部点权都是1,那么一种方案只有0和1两种取值,考虑这个状态设计:\(f......
  • [ARC167D] Good Permutation 题解
    题意对于一个长度为\(N\)的排列\(Q\),定义其为好的,当且仅当对于任意整数\(i\in\left[1,N\right]\),在进行若干次操作\(i\leftarrowQ_i\)后可以得到\(i=1\)。给定一个排列\(P\),定义一次操作为交换两个数。定义\(M\)为可以将\(P\)变为一个好的的排列的最小操......
  • 计算机网络的分组转发算法例题解析
    例题展示例题解决将题目中要求的ip地址与相对应的子网掩码进行二进制上面的相与即可,若是与目的ip地址一致,那么就直接跳转到其对应的那个接口;否则就直接跳转到默认接口;本题答案为R2;......
  • UVA1366 Martian Mining 题解
    这个题可以用动态规划解决。令\(sbe_{i,j}\)为第\(j\)列\(1\)至\(i\)个格子\(BE\)矿总和,令\(snw_{i,j}\)为第\(i\)行\(1\)至\(j\)个格子\(NEW\)矿总和。\(dp_{i,j,0}\)表示为以第(\(i\),\(j\))为右下角,第(\(i\),\(j\))号格子建立\(BE\)矿管道的最大采矿量。\(dp......