CF 1334(edu85) F. Strange Function(线段树,dp)

Posted fzuzyz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF 1334(edu85) F. Strange Function(线段树,dp)相关的知识,希望对你有一定的参考价值。

链接:https://codeforces.com/contest/1334/problem/F

  题意:定义函数f,对于一个含n个元素的数组arr,首先有一个空的数组c,按顺序对arr数组元素进行操作,若ai大于c数组所有元素,则将arr[i]加入c数组末尾,最后得到的数组c=f(arr)。现在给你一个数组a,长度为n,一个数组b,长度为m,删除a的第i项代价为pi(pi可以为负),求删除a中若干元素,使f(a)=b得最小代价为多少,若没有输出NO。

  解题思路:作为edu85的F题,套路和edu84的F题居然一模一样,都是线段数维护dp。不难看出b是个递增序列,答案存在问题其实可以O(n)判断。我们设dp[i][j]为数组a的前i个元素组成的子数组,经过删除后,经过f变换后得到的数组的为b数组前j项时的最小代价。那么转移方程分$a_{i}<b_{j}$,$a_{i}=b_{j}$,$a_{i}>b_{j}$三种情况,若$a_{i}<b_{j}$,第i项元素可以删除也可以不删除,因为该元素不会加入新数组,对结果不产生影响,可以得到$dp[i][j]=dp[i-1][j]+max(0,p_{i})$,而对于$a_{i}>b_{j}$,情况更为简单,$a_{i}$必须删除,因此$dp[i][j]=dp[i-1][j]+p_{i}$,而对于$a_{i}=b_{j}$,情况较为复杂,他可以由dp[i-1][j-1]不删除得到,也可以由dp[i-1][j]进行删除或不删除得到,有三种可能,即$dp[i][j]=min(dp[i-1][j-1]+p_{i},dp[i-1][j],dp[i-1][j]+p_{i})$,因此我们得到了一个时间复杂度为$O(nm)$的dp,但是可以发现,$a_{i}<b_{j}$与$a_{i}>b_{j}$两种情况的j必然是两个连续的区间,并且都有dp[i-1][j]转移,而二者所加的$p_{i}$以及$max(0,p_{i})$也都是常数,可以用线段树维护,而$a_{i}=b_{j}$的情况也只有一个点,单点更新即可。因此直接用线段树进行维护,将二维dp退化为一维的线段树。初始化dp[0]=0,其余为无穷大,随后进行区间修改,时间复杂度$O(nlogm)$

  比赛的时候看到这个题几乎是立马发现做法,因为之前的edu84F题看了dls的解说,学会线段树维护dp这个套路,但是只剩10分钟了,没来得及打,有点可惜。

  最后贴上AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair <ll,ll> pii;
#define rep(i,x,y) for(int i=x;i<y;i++)
#define rept(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)
#define all(x) x.begin(),x.end()
#define pb push_back
#define fi first
#define se second
#define mes(a,b) memset(a,b,sizeof a)
#define mp make_pair
#define dd(x) cout<<#x<<"="<<x<<" "
#define de(x) cout<<#x<<"="<<x<<"
"
#define debug() cout<<"I love Miyamizu Mitsuha forever.
"
const ll inf=1e18;
const int maxn=5e5+5;
map<ll,ll> pos;
ll p[maxn];
ll a[maxn],b[maxn];
class element
{
    public:
        int l,r;
        ll plus,val;
};
 
class Tree
{
    public:
        element tree[maxn<<2];
        void build(int id,int l,int r)
        {
            tree[id].l=l;
            tree[id].r=r;
            tree[id].plus=0;
            if(l==r)
            {
                tree[id].val=inf;
                return ;
            }
            int mid=(l+r)/2;
            build(id*2,l,mid);
            build(id*2+1,mid+1,r);
        }
        void update(int id,int p,ll num)
        {
            if(tree[id].l==p&&tree[id].r==p)
            {
                tree[id].val=num;
                return ;
            }
            if(tree[id].l>p||tree[id].r<p) return ;
            push_down(id);
            update(id*2,p,num);
            update(id*2+1,p,num);    
        }
        void add(int id,int l,int r,ll num)
        {
            if(tree[id].l>=l&&tree[id].r<=r)
            {
                tree[id].plus+=num;
                tree[id].val+=num*(tree[id].r-tree[id].l+1);
                return ;
            }
            if(tree[id].l>r||tree[id].r<l) return ;
            push_down(id);
            if(tree[id*2].r>=l) add(id*2,l,r,num);
            if(tree[id*2+1].l<=r) add(id*2+1,l,r,num);
        }
        void push_down(int id)
        {
            if(tree[id].l!=tree[id].r)
                rept(j,id*2,id*2+1)
                {
                    tree[j].val+=tree[id].plus*(tree[j].r-tree[j].l+1);
                    tree[j].plus+=tree[id].plus;
                }
            tree[id].plus=0;
        }
        ll query(int id,int l,int r)
        {
            if(tree[id].l>=l&&tree[id].r<=r) return tree[id].val;
            if(tree[id].l>r||tree[id].r<l) return 0;
            ll s=0;
            push_down(id);
            if(tree[id*2].r>=l) s+=query(id*2,l,r);
            if(tree[id*2+1].l<=r) s+=query(id*2+1,l,r);
            return s;
        }
} dp;
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;
    cin>>n;
    rept(i,1,n) cin>>a[i];
    rept(i,1,n) cin>>p[i];
    cin>>m;
    rept(i,1,m)
    {
        cin>>b[i];
        pos[b[i]]=i;
    }
    dp.build(1,0,m);
    dp.update(1,0,0);
    rept(i,1,n)
    {
        if(pos[a[i]]==0)
        {
            if(p[i]<0)
            {
                dp.add(1,0,m,p[i]);
                continue;
            }
            ll *pp=lower_bound(b+1,b+1+m,a[i]);
            ll ppp=pp-b;
            dp.add(1,0,ppp-1,p[i]);
        }
        else
        {
            int pp=pos[a[i]];
            ll val1=dp.query(1,pp,pp),val2=dp.query(1,pp-1,pp-1);
            dp.update(1,pp,min(val1+min(0ll,p[i]),val2));
            if(pp!=m&&p[i]<0) dp.add(1,pp+1,m,p[i]);
            dp.add(1,0,pp-1,p[i]);
        }
    }
    ll ans=dp.query(1,m,m);
    if(ans<=5e14) cout<<"YES
"<<ans<<"
";
    else cout<<"NO
";
    return 0;
}

 

以上是关于CF 1334(edu85) F. Strange Function(线段树,dp)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces1539 F. Strange Array(思维,线段树)

Codeforces Round #727 (Div. 2) F. Strange Array(思维,线段树子段和)

CF1383E Strange Operation

CF 873C Strange Game On Matrix(贪心)

题解 CF1383E Strange Operation

cf842d Vitya and Strange Lesson