• 【CF1257E】The Contest【线段树】


    题意:给定三个序列abc,问最少操作几次使得满足a<b<c

    题解:将三个序列合并起来,设cnt[i][1/2/3]表示前i个数有几个是来自序列1/2/3的。

    枚举第一个序列要到i,此时对于第一个序列的操作次数就是cnt[i][2]+cnt[i][3]+cnt[n][1]-cnt[i][1]

    对于第二个序列,暴力枚举要到j,此时的操作次数就是cnt[j][3]-cnt[i][3]+cnt[n][2]-cnt[j][2]

    将两个加起来就是答案,求出最小的那个

    显然这样做是O(n^2)的,考虑优化

    可以观察到,对于一个确定的i,cnt[i][3]是定值,将式子改写为cnt[j][3]+cnt[n][2]-cnt[j][2]-cnt[i][3],设f[i]=cnt[i][3]+cnt[n][2]-cnt[i][2],那么f[i]是一个确定的函数,式子进一步改写为f[j]-cnt[i][3]

    相当于对于每个i,求一个最小的f[j]-cnt[i][3],用一个线段树即可

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<cmath>
    #define ll long long
    using namespace std;
    int k1,k2,k3,a,n,ans=1e9;
    int cnt[200001][4];
    struct node
    {
        int v,bh;
    }q[200001];
    bool cmp(const node &T1,const node &T2){return T1.v<T2.v;}
    class Segtree
    {
    public:
        int v[200001*5],fl[200001*5];
     
        void pushup(int fxy)
        {
          v[fxy]=min(v[fxy<<1],v[fxy<<1|1]);
        }
        void pushdown(int fxy)
        {
          if(fl[fxy])
          {
            v[fxy<<1]+=fl[fxy];
            v[fxy<<1|1]+=fl[fxy];
            fl[fxy<<1]+=fl[fxy];
            fl[fxy<<1|1]+=fl[fxy];
            fl[fxy]=0;
          }
        }
        void build(int l,int r,int fxy)
        {
          if(l==r)
          {
            v[fxy]=cnt[l][3]+cnt[n][2]-cnt[r][2];
            return;
          }
          int mid=l+r>>1;
          build(l,mid,fxy<<1);
          build(mid+1,r,fxy<<1|1);
          pushup(fxy);
        }
        void change(int l,int r,int al,int ar,int tv,int fxy)
        {
            if(l==al && r==ar)
            {
              v[fxy]+=tv;
              fl[fxy]+=tv;
              return;
            }
            pushdown(fxy);
            int mid=l+r>>1;
            if(ar<=mid)change(l,mid,al,ar,tv,fxy<<1);
            if(al>mid)change(mid+1,r,al,ar,tv,fxy<<1|1);
            if(al<=mid && ar>mid)
            {
              change(l,mid,al,mid,tv,fxy<<1);
              change(mid+1,r,mid+1,ar,tv,fxy<<1|1);
            }
            pushup(fxy);
        }
        int ask(int l,int r,int al,int ar,int fxy)
        {
            if(l==al && r==ar)return v[fxy];
            pushdown(fxy);
            int mid=l+r>>1;
            if(ar<=mid)return ask(l,mid,al,ar,fxy<<1);
            if(al>mid)return ask(mid+1,r,al,ar,fxy<<1|1);
            return min(ask(l,mid,al,mid,fxy<<1),ask(mid+1,r,mid+1,ar,fxy<<1|1));
        }
    }segtree;
    int main()
    {
        scanf("%d%d%d",&k1,&k2,&k3);
        for(int i=1;i<=k1;i++){scanf("%d",&a);q[i]=(node){a,1};}
        for(int i=1;i<=k2;i++){scanf("%d",&a);q[i+k1]=(node){a,2};}
        for(int i=1;i<=k3;i++){scanf("%d",&a);q[i+k1+k2]=(node){a,3};}
        n=k1+k2+k3;
        sort(q+1,q+1+n,cmp);
        for(int i=1;i<=n;i++)
        {
          cnt[i][1]=cnt[i-1][1]+(q[i].bh==1);
          cnt[i][2]=cnt[i-1][2]+(q[i].bh==2);
          cnt[i][3]=cnt[i-1][3]+(q[i].bh==3);
        }
        int t;
        segtree.build(1,n,1);
        for(int i=0;i<n;i++)
        {
          t=segtree.ask(1,n,i+1,n,1);
          t=min(t,cnt[n][2]-cnt[i][2]);
          ans=min(ans,t+cnt[i][2]+cnt[i][3]+cnt[n][1]-cnt[i][1]);
          if(q[i+1].bh==3)segtree.change(1,n,i+1,n,-1,1);
        }
        ans=min(ans,cnt[n][2]+cnt[n][3]);
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    C++结构体内重载、this指针和友元函数(初步了解)
    数据结构—造树计划—二叉搜索树
    PTA顺序的分数
    PTA兼容任务
    PTA航船
    UML-基于GRASP对象设计步骤
    UML-设计对象时涉及的制品有哪些?
    UML-什么是用例实现(场景实现)?
    UML-如何使用GRASP进行对象设计?
    日志总结
  • 原文地址:https://www.cnblogs.com/worcher/p/11865208.html
Copyright © 2020-2023  润新知