• 【Foreign】划分序列 [线段树][DP]


    划分序列

    Time Limit: 20 Sec  Memory Limit: 256 MB

    Description

      

    Input

      

    Output

      仅一行一个整数表示答案。

    Sample Input

      9 4
      1 1 1 3 2 2 1 3 1

    Sample Output

      5

    HINT

      

    Main idea

      将序列分为若干段,使得和最大的那一段最小,值可以为负。

    Source

      首先,显然都想到了二分答案

      我们先把都为正数或负数的情况写了:Ai>=0的时候求出最小的划分段数x,若x<=K则表示当前答案可行;Ai<=0的时候求出最大的划分段数x,若x>=K则表示当前答案可行。然后再打了暴力,接着我们对拍一下,惊讶地发现了一个规律:若最小划分段数为L,最大划分段数为R,当L<=K<=R时则可以更新

      然后我们用DP来求L和R,也就是:若一段的和满足<=mid,则可以分为一段。

      然后我们发现,可以用线段树优化寻找1~i-1中的最小值或最大值,这样判断就可以满足效率了。

    Code

      1 #include<iostream>  
      2 #include<algorithm>  
      3 #include<cstdio>  
      4 #include<cstring>  
      5 #include<cstdlib>  
      6 #include<cmath>  
      7 #include<vector>
      8 using namespace std;
      9 typedef long long s64;
     10 
     11 const int INF = 2147483640;
     12 const int ONE = 5e4+5;
     13 
     14 int n,block;
     15 int L,R;
     16 int x,sum[ONE],s[ONE];
     17 int li[ONE],li_num; 
     18 int f_min[ONE],f_max[ONE];
     19 int res_min,res_max;
     20 int Zero;
     21 
     22 int get()
     23 {
     24         int res=1,Q=1;    char c;
     25         while( (c=getchar())<48 || c>57)
     26         if(c=='-')Q=-1;
     27         if(Q) res=c-48; 
     28         while((c=getchar())>=48 && c<=57) 
     29         res=res*10+c-48; 
     30         return res*Q; 
     31 }
     32 
     33 namespace Seg
     34 {
     35         struct power
     36         {
     37             int minn;
     38             int maxx;
     39         }Node[ONE];
     40         
     41         void Build(int i,int l,int r)
     42         {
     43             Node[i].minn = INF;
     44             Node[i].maxx = -INF;
     45             if(l==r) return;
     46             int mid=(l+r)>>1;
     47             Build(i<<1,l,mid);    Build(i<<1|1,mid+1,r);
     48         }
     49         
     50         void Update(int i,int l,int r,int L,int x,int PD)
     51         {
     52             if(l==r)
     53             {
     54                 if(!PD) Node[i].minn = x;
     55                 else Node[i].maxx = x;
     56                 return;
     57             }
     58             int mid=(l+r)>>1;
     59             if(L<=mid) Update(i<<1,l,mid,L,x,PD);
     60             else Update(i<<1|1,mid+1,r,L,x,PD);
     61             Node[i].minn = min(Node[i<<1].minn, Node[i<<1|1].minn);
     62             Node[i].maxx = max(Node[i<<1].maxx, Node[i<<1|1].maxx);
     63         }
     64         
     65         void Query(int i,int l,int r,int L,int R)
     66         {
     67             if(L<=l && r<=R)
     68             {
     69                 res_min=min(res_min, Node[i].minn);
     70                 res_max=max(res_max, Node[i].maxx);
     71                 return;
     72             }
     73             int mid=(l+r)>>1;
     74             if(L<=mid) Query(i<<1,l,mid,L,R);
     75             if(mid+1<=R) Query(i<<1|1,mid+1,r,L,R);
     76         }
     77 }
     78 
     79 int Check(int mid)
     80 {
     81         Seg::Build(1,1,li_num);
     82         Seg::Update(1,1,li_num, Zero,0,0);
     83         Seg::Update(1,1,li_num, Zero,0,1);
     84         for(int i=1;i<=n;i++)
     85         {
     86             int pos = lower_bound(li+1,li+li_num+1,sum[i] - mid) - li;
     87             res_min = INF;    res_max = -INF;    Seg::Query(1,1,li_num, 1,pos);
     88             f_min[i] = res_min + 1;
     89             f_max[i] = res_max + 1;
     90             Seg::Update(1,1,li_num, s[i],f_min[i],0);
     91             Seg::Update(1,1,li_num, s[i],f_max[i],1);
     92         }
     93         return (f_min[n]<=block && block<=f_max[n]);
     94 }
     95 
     96 
     97 int main()
     98 {      
     99         n=get();    block=get();
    100         li[++li_num] = 0;
    101         for(int i=1;i<=n;i++)
    102         {
    103             x=get();
    104             li[++li_num] = sum[i] = sum[i-1] + x;
    105             if(x < 0) L+=x; else R+=x; 
    106         }
    107         
    108         sort(li+1,li+li_num+1);
    109         li_num = unique(li+1,li+li_num+1) - li - 1;
    110         
    111         for(int i=1;i<=n;i++)
    112             s[i]=lower_bound(li+1,li+li_num+1, sum[i]) - li;
    113         Zero = lower_bound(li+1,li+li_num+1, 0) - li;
    114         
    115         while(L < R - 1)
    116         {
    117             int mid=(L+R)>>1;
    118             if(Check(mid)) R = mid;
    119             else L = mid;
    120         }
    121         
    122         if(Check(L)) printf("%d",L);
    123         else printf("%d",R);
    124 }
    View Code
  • 相关阅读:
    windows命令行下导入excel数据到SQLite数据库
    Android Studio如何提示函数用法
    在不root手机的情况上读取Data目录上的文件
    OSI七层模型
    设计模式之代理模式
    Android中Javascript中的调用
    cf #205 B Codeforces Round #205 (Div. 2) B. Two Heaps
    uva 10600 次小生成树
    防2B && 图论模板 && 7788
    最大匹配 && 最佳完美匹配 模板
  • 原文地址:https://www.cnblogs.com/BearChild/p/6532994.html
Copyright © 2020-2023  润新知