zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    EBS 获取用户密码
    Using Create directory & UTL_FILE in Oracle
    Loops with PL/SQL
    Create trigger
    Oracle DB解锁和 rerun FA depreciation
    oracle中数组的运用
    FNDLOAD使用大全
    不安装Oracle客户端,透过PL/SQL Developer连接Server DB
    R12组织屏蔽
    Oracle DB Link创建
  • 原文地址:https://www.cnblogs.com/FZUzyz/p/12772821.html
Copyright © 2011-2022 走看看