arc159_F DP

Posted 0922-Blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了arc159_F DP相关的知识,希望对你有一定的参考价值。

题意(简化版)

给出一个长度为 \\(2n\\) 的序列 \\(a_i\\),求将序列分割为若干个长度为偶数的区间,满足每个区间内都不含绝对众数(出现次数严格大于长度的一半的数)的方案数。

\\(n\\le 500000,\\,a_i\\le2n\\)

解法

解法和官方题解大致相同,虽然官方题解我也没看太明白(

显然一定在偶数出断开,所以可以先将序列两两分组,即 \\(p_i=(a_2i-1, a_2i)\\) ,变成由 \\(n\\) 个二元组组成的序列。

为方便叙述,这里将二元组 \\(p_i\\) 的第一个元素记作 \\(x_i\\),第二个记作 \\(y_i\\)

考虑 dp,用 \\(f_i\\) 表示前 \\(i\\) 个二元组的合法分割方案数,那么 \\(f_i = \\sum\\limits_(j, i]\\,is\\,available f_j\\),发现统计合法的转移是一个比较困难的工作,考虑正难则反,使用总和减去不合法的转移,那么

\\[f_i = \\sum_j=1^i-1f_j-\\sum_(j,i]\\;is\\;not\\; availablef_j \\]

那么如何统计不合法的转移呢,首先发现每个不合法的转移中只会有一个绝对众数,那么可以考虑枚举绝对众数,对于绝对众数 \\(v\\) 分别统计。

具体统计时,需要使用一个我不知道的绝对众数经典 trick,对于每个二元组 \\(p_i\\) 赋一个权值 \\(w_i\\):若 \\(x_i\\)\\(y_i\\) 都等于 \\(v\\)\\(w_i = 1\\);若 \\(x_i\\)\\(y_i\\) 其中有一个为 \\(v\\)\\(w_i=0\\);若 \\(x_i\\)\\(y_i\\) 都不为 \\(v\\)\\(w_i=-1\\)

那么 \\(v\\) 为某一区间的绝对众数当且仅当该段区间的权值和为正,所以可以对权值做前缀和。

于是当前 dp 到 \\(i\\),枚举的绝对众数为 \\(v\\) 时,我们可以 对于每一权值前缀和 \\(val\\) 统计其对应的所有 \\(j\\)\\(f_j\\) 之和 \\(memo_val\\),即 \\(memo_val = \\sum\\limits_j=1^n\\left[\\sum\\limits_k=1^j w_k= val\\right] f_j\\),由于 \\(i\\) 的权值前缀和 \\(s\\) 的变化是连续的,\\(memo\\) 的前缀和很容易维护。减去不合法方案只需让 \\(f_i\\) 减去 \\(memo\\) 的前 \\(s-1\\) 项的前缀和。

于是我们便得到了一个 \\(\\mathcalO(n^2)\\) 的算法。

如何优化呢,发现我们 dp 时做了许多无用的操作,其实可以先对于每个绝对众数 \\(v\\) 求出所有有可能以 \\(v\\) 为绝对众数的区间的并集,这个并集大小是与 \\(v\\) 的出现次数同阶的,于是对于所有 \\(v\\),这个并集大小的和是 \\(\\mathcal O(n)\\) 的!

对于每个 \\(v\\),它的并集一定是由多个彼此不相连的区间构成的,对于每个区间分别统计其 \\(memo\\) 并转移即可。

时间复杂度 \\(\\mathcalO(n)\\)

ARC073FMany Moves

题目

一个显然的(dp),设(dp_{i,j})表示其中一个棋子在(x_i)点,另一个棋子在(j)点的最小花费

显然(dp_{i,j})有两种转移

第一种是把(x_i)上的棋子移到(x_{i+1}),那么那么就是(dp_{i+1,j}=min(dp_{i,j}+|x_{i+1}-x_i|))

第二种就是把(j)上的棋子移动到(x_{i+1}),那么就是(dp_{i+1,x_i}=min(dp_{i,j}+|j-x_{i+1}|))

这是(O(nQ))的,考虑优化

发现第一种转移形式非常固定,于是直接整体(dp)。第一种转移其实就是全局加,我们维护一个加法标记就可以了。

第二种转移都是转移到(dp_{i+1,x_i}),绝对值看起来比较讨厌,考虑将绝对值拆开,当(jgeq x_{i+1})时,(dp_{i+1,x_i}=dp_{i,j}+j-x_{i+1});当(jleq x_{i+1})时,(dp_{i+1,x_i}=dp_{i,j}-j+x_{i+1}),于是考虑直接使用线段树来维护(dp_{i,j}+j)(dp_{i,j}-j)的最小值,查一下这两个区间就能转移了。

代码

#include<bits/stdc++.h>
#define re register
#define LL long long
inline int read() {
    char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const LL inf=1e15;
const int maxn=2e5+5;
int l[maxn<<2],r[maxn<<2];LL mx[maxn<<2][2],tag;
int pos[maxn],d[maxn],n,A,B,Q;
inline LL min(LL a,LL b) {return a<b?a:b;}
void build(int x,int y,int i) {
    l[i]=x,r[i]=y;mx[i][0]=mx[i][1]=inf;
    if(x==y) {pos[x]=i;return;}
    int mid=x+y>>1;
    build(x,mid,i<<1),build(mid+1,y,i<<1|1);
}
void change(int x,LL v,int o) {
    x=pos[x];mx[x][o]=min(v,mx[x][o]);x>>=1;
    while(x) mx[x][o]=min(mx[x][o],v),x>>=1;
}
LL query(int x,int y,int i,int o) {
    if(x<=l[i]&&y>=r[i]) return mx[i][o];
    int mid=l[i]+r[i]>>1;LL now=inf;
    if(x<=mid) now=min(now,query(x,y,i<<1,o));
    if(y>mid) now=min(now,query(x,y,i<<1|1,o));
    return now;
}
inline LL ABS(LL x) {return x>=0?x:-x;}
int main() {
    n=read(),Q=read(),A=read(),B=read();
    for(re int i=1;i<=Q;i++) d[i]=read();
    build(1,n,1);
    change(A,ABS(d[1]-B)+A,0);change(A,ABS(d[1]-B)-A,1);
    change(B,ABS(d[1]-A)+B,0);change(B,ABS(d[1]-A)-B,1);
    for(re int i=2;i<=Q;i++) {
        LL x=query(d[i],n,1,0)-d[i],y=query(1,d[i],1,1)+d[i];
        x=min(x,y);
        LL a=min(mx[pos[d[i-1]]][0]-d[i-1],mx[pos[d[i-1]]][1]+d[i-1]);
        if(x<a+ABS(d[i]-d[i-1])) {
            x+=tag;tag+=ABS(d[i]-d[i-1]);x-=tag;
            change(d[i-1],x+d[i-1],0),change(d[i-1],x-d[i-1],1);
        }
        else tag+=ABS(d[i]-d[i-1]);
    }
    LL ans=inf;
    for(re int i=1;i<=n;i++) ans=min(ans,mx[pos[i]][1]+i);
    printf("%lld
",ans+tag);
    return 0;
}

以上是关于arc159_F DP的主要内容,如果未能解决你的问题,请参考以下文章

atc abc159FF - Knapsack for All Segments(dp优化)

AGC/ARC

Arc073_F Many Moves

arc159b

ARC073FMany Moves

ARC 68F - Solitaire(dp)