• Codeforces 786C Till I Collapse


    题意:
    给出一个长度为n的序列,每个数值在1-n之间且为整数,现在要把这个序列划分为若干段,使得每一段的颜色种数不超过k,求最少的区间数目.对于从1到n的n种k的取值,分别输出这时的最少区间数目.
    分析:
    首先这个题很像HH的项链,而HH的项链的在线做法需要写可持久化线段树,我们自然想到这个题也需要可持久化线段树
    考虑对于每个k分别求解.暴力的贪心做法是这样:从左端开始,每次找出一段尽量长的颜色种数不超过k的区间划分出来.如果颜色种数要求不超过k,那么划分的区间段数最多为n/k上取整.(k个元素一段即可).对于k的n种取值,一共最多要找nln(n)段区间.如果能在较低的复杂度(例如logn)内找出每一段区间,这个题就做出来了.
    然后我的想法是类似bzoj4504 K个串,写了一发区间修改的可持久化线段树,然后每次在线段树上二分.Codeforces的官方题解大概相当于这个做法差分一下,不需要区间修改,只需要单点修改的可持久化线段树.
    具体是:对于每个左端点L,我们求出一个数组f[L][],f[L][i]存储从L到i出现的不同颜色的种数(如果i<L那么f[L][i]=0).那么从F[L][]到F[L-1][],只有a[L-1]影响的一段区间的数值会+1,那么可以用可持久化线段树来维护这个序列.查询时如果二分答案,每次在线段树上查询是两个log,直接在线段树上二分(也就是在每个节点判断向左侧还是右侧递归求解)就是一个log的.我写了一发标记永久化.细节参见代码.

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int maxn=100005;
    struct node{
      int Max,mk;
      node* ch[2];
      node(){}
      node(int x,int y){
        ch[0]=ch[1]=0;Max=x;mk=y;
      }
    }t[maxn*60];int tsz=0;
    node* root[maxn];
    node* newnode(int x,int y){
      t[++tsz]=node(x,y);return t+tsz;
    }
    int a[maxn];
    int last[maxn];
    void Insert(node* rt0,node* &rt,int l,int r,int ql,int qr){
      if(ql<=l&&r<=qr){
        rt=newnode(rt0->Max+1,rt0->mk+1);
        rt->ch[0]=rt0->ch[0];rt->ch[1]=rt0->ch[1];
      }else{
        rt=newnode(0,rt0->mk);
        rt->ch[0]=rt0->ch[0];rt->ch[1]=rt0->ch[1];
        int mid=(l+r)>>1;
        if(ql<=mid)Insert(rt0->ch[0],rt->ch[0],l,mid,ql,qr);
        if(qr>mid)Insert(rt0->ch[1],rt->ch[1],mid+1,r,ql,qr);
        rt->Max=max(rt->ch[0]->Max,rt->ch[1]->Max)+rt0->mk;
      }
    }
    int n;
    int query(node* rt,int l,int r,int lim,int pre){
      if(l==r){//printf("%d
    ",pre);
        if(pre+rt->Max>lim)return -1;
        else return l;
      }
      int lmax=rt->ch[0]->Max+pre+rt->mk;
      int mid=(l+r)>>1;
      if(lmax>lim)return query(rt->ch[0],l,mid,lim,pre+rt->mk);
      else if(lmax<lim)return query(rt->ch[1],mid+1,r,lim,pre+rt->mk);
      int t=query(rt->ch[1],mid+1,r,lim,pre+rt->mk);
      if(t==-1)return mid;
      else return t;
    }
    int right(int s,int lim){
      return query(root[s],1,n,lim,0);
    }
    int work(int lim){
      int pt=1;
      int ans=0;
      while(pt<=n){
        ans++;
        pt=right(pt,lim)+1;//if(lim==1)printf("%d
    ",pt);
      }
      return ans;
    }
    int main(){
      scanf("%d",&n);
      for(int i=1;i<=n;++i)scanf("%d",a+i);
      root[n+1]=newnode(0,0);
      root[n+1]->ch[0]=root[n+1]->ch[1]=root[n+1];
      for(int i=1;i<=n;++i)last[i]=n+1;
      for(int i=n;i>=1;--i){
        root[i]=root[i+1];
        Insert(root[i],root[i],1,n,i,last[a[i]]-1);
        last[a[i]]=i;
      }
      //  printf("%d
    ",query(root[1],1,n,1,0));return 0;
      for(int i=1;i<=n;++i){
        printf("%d ",work(i));
      }
      return 0;
    }
    
    
  • 相关阅读:
    Spark2.x学习笔记:Spark SQL程序设计
    Spark2.x学习笔记:Spark SQL的SQL
    Spark2.x学习笔记:Spark SQL快速入门
    Hystrix入门执行过程
    redis配置master-slave模式
    MockServer 入门
    Locust分布式负载测试工具入门
    poi读取excel元素
    hadoop+spark集群搭建入门
    Vi文档
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6952508.html
Copyright © 2020-2023  润新知