zoukankan      html  css  js  c++  java
  • BZOJ 4367 [IOI2014]holiday (决策单调DP+主席树+分治)

    题目大意:略 题目传送门

    神题,不写长题解简直是浪费了这道题

    贪心

    考虑从0节点出发的情况,显然一直往前走不回头才是最优策略

    如果起点是在中间某个节点$s$,容易想到,如果既要游览$s$左边的某些景点,又要游览$s$右边的某些景点,最优策略一定是先游览完一边,然后再穿过$s$节点去游览另一边

    也就是说$s$节点一定只被穿过一次,且先被游览完的一边还要额外消耗$x$点代价,$x$是距离$s$最远的被游览的节点到s的距离

    设计DP状态

    现在只考虑从S出发往右走的情况

    定义状态$dp[i][j]$表示第i天,现在在j点时最多游览的景点数

    容易得到转移方程$dp[i][j]=max(dp[i-|j-k|][k]+|j-k|+a_{j})$

    暴力枚举 $O(n^{3})$,线段树优化$DP;;O(n^{2}logn)$

    两种状态的空间是$O(n^{2})$的,不论是空间还是时间都不可行

    利用上述贪心结论,我们一定一直往前走不回头,除非我们回头到$s$然后走另一边

    所以j这一维不用记录了,因为在路上走的距离总和是已知的

    除了向右走的情况,还有向左走,向右走回到景点$s$,向左走回到景点$s$另外三种情况,设它们为$f[i][0],g[i][0],f[i][1],g[i][1]$,转移都很类似不过多赘述

    缩减状态,$dp[i]$表示走了$i$天最多游览的景点数

    枚举游览的最右侧端点$j$,$dp[i]$就是在$S$到$j$之间选择最大的$i-(j-S)$个景点

    可以用可持久化权值线段树实现,每次查询在主席树上二分,时间$O(logn)$

    决策单调性

    上述过程依然没有解决时间$O(n^{2})$这一难题

    假设我们现在要求解$f[i]$,即走了i天最多游览的景点数

    暴力枚举所有位置,找到了决策位置$p$,

    那么走$j leq i-1$天时,$f[j]leq f[i]$,且$f[j]$选择的点的集合一定是$S$的子集,$f[j]$的决策位置一定$leq p$

    走$j geq i+1$天时,$f[j]geq f[i]$,且$f[j]$选择的点的集合一定包含$S$,$f[j]$的决策位置一定$geq p$

    发现决策竟然是单调的

    分治

    用类似于[WF2017]Money for Nothing的方法分治

    但这道题对有多个决策位置的处理方式略有不同

    在分治过程中,设现在天数的分治区间是$[l1,r1]$,选择要求解$f[mid]$,可能的决策区间是$[l2,r2]$

    暴力枚举$[l2,r2]$,找到了$f[mid]$的决策集合$T$,$T$中的每个元素都能作为$f[mid]$的决策,设其中最靠右的元素是b

    $[l1,mid-1]$可能的决策区间是$[l2,b]$,因为并不知道哪个决策位置对应的点集合$S$的子集最优,所以$f[mid]$整个决策区间都可能作为$[l1,mid-1]$的决策

    $[mid+1,r1]$可能的决策区间是$[b,r2]$,而不是集合里最靠左的元素到$r2$,下面给出简单证明

    现在点权序列是2 8 9 1 3 11,走7步,最优方案有两种,决策位置是5,对应的点集$S$是8 9 3,决策位置是6,对应的点集$S$是9 11

    容易发现,更靠右的决策位置,除了对应点集S里的点,剩余的权值较大的节点更多

    $f[mid]$决策位置是5时,对应的点集S是8 9 3,还能被取的点是2 1

    $f[mid]$决策位置是6时,对应的点集S是9 11,还能被取的点是8 2 1 3

    因为大家都是贪心取,决策位置左边的最大的都取走了

    最后把四种情况合并即可

    另外这道题卡空间,需要对权值离散,不离散的话主席树树高会非常高,空间上容易被卡

    别忘了离散只是为了优化线段树的结构,统计的时候把实际值还原回来

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 #define N1 100010
      5 #define M1 250010
      6 #define ll long long
      7 #define dd double
      8 #define inf 23333333333333333ll
      9 using namespace std;
     10  
     11 int gint()
     12 {
     13     int ret=0,fh=1;char c=getchar();
     14     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
     15     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
     16     return ret*fh;
     17 }
     18 int b[N1];
     19 struct Persist_SEG{
     20 int sz[N1*22],ls[N1*22],rs[N1*22],root[N1],tot; ll sum[N1*22];
     21 inline void pushup(int rt)
     22 {
     23     sz[rt]=sz[ls[rt]]+sz[rs[rt]];
     24     sum[rt]=sum[ls[rt]]+sum[rs[rt]];
     25 }
     26 void update(int x,int l,int r,int rt1,int &rt2,int w)
     27 {
     28     if(!rt2||rt2==rt1){ rt2=++tot; sz[rt2]=sz[rt1]; ls[rt2]=ls[rt1]; rs[rt2]=rs[rt1]; sum[rt2]=sum[rt1]; }
     29     if(l==r){ sum[rt2]+=w; sz[rt2]++; return; }
     30     int mid=(l+r)>>1; ll ans=0;
     31     if(x<=mid) update(x,l,mid,ls[rt1],ls[rt2],w);
     32     else update(x,mid+1,r,rs[rt1],rs[rt2],w);
     33     pushup(rt2);
     34 }
     35 ll query(int K,int l,int r,int rt1,int rt2)
     36 {
     37     if(!rt2||!K) return 0;
     38     if(l==r) return min(sum[rt2]-sum[rt1],1ll*K*b[l]);
     39     int mid=(l+r)>>1; ll ans=0;
     40     if(sz[rs[rt2]]-sz[rs[rt1]]<K) ans=sum[rs[rt2]]-sum[rs[rt1]]+query(K-(sz[rs[rt2]]-sz[rs[rt1]]),l,mid,ls[rt1],ls[rt2]); 
     41     else ans=query(K,mid+1,r,rs[rt1],rs[rt2]); 
     42     return ans;
     43 }
     44 }s;
     45  
     46 int n,S,D,ma,de;
     47 int a[N1];
     48 ll f[M1][2],g[M1][2];
     49 void solve_f0(int l1,int r1,int l2,int r2)
     50 {
     51     if(l1>r1||l2>r2) return;
     52     int mid=(l1+r1)>>1,i,p=-1; ll tmp,ans=-inf;
     53     for(i=l2;i<=r2&&i-S<=mid;i++)
     54     {
     55         tmp=s.query(mid-(i-S),1,ma,s.root[S-1],s.root[i]);
     56         if(tmp>ans) ans=tmp,p=i;
     57         else if(tmp==ans) p=i;
     58     }
     59     f[mid][0]=ans;
     60     if(ans==-inf)
     61     {
     62         for(i=mid+1;i<=r1;i++) if(i>=(l2-S)){ solve_f0(i,r1,l2,r2); break; }
     63         return;
     64     }
     65     solve_f0(l1,mid-1,l2,p); solve_f0(mid+1,r1,p,r2);
     66 }
     67 void solve_f1(int l1,int r1,int l2,int r2)
     68 {
     69     if(l1>r1||l2>r2) return;
     70     int mid=(l1+r1)>>1,i,p=-1; ll tmp,ans=-inf;
     71     for(i=l2;i<=r2&&2*(i-S)<=mid;i++)
     72     {
     73         tmp=s.query(mid-2*(i-S),1,ma,s.root[S-1],s.root[i]);
     74         if(tmp>ans) ans=tmp,p=i;
     75         else if(tmp==ans) p=i;
     76     }
     77     f[mid][1]=ans;
     78     if(ans==-inf)
     79     {
     80         for(i=mid+1;i<=r1;i++) if(i>=2*(l2-S)){ solve_f1(i,r1,l2,r2); break; }
     81         return;
     82     }
     83     solve_f1(l1,mid-1,l2,p); solve_f1(mid+1,r1,p,r2);
     84 }
     85 void solve_g0(int l1,int r1,int l2,int r2)
     86 {
     87     if(l1>r1||l2>r2) return;
     88     int mid=(l1+r1)>>1,i,p=-1; ll tmp,ans=-inf;
     89     for(i=r2;i>=l2&&S-i<=mid;i--)
     90     {
     91         tmp=s.query(mid-(S-i),1,ma,s.root[i-1],s.root[S-1]);
     92         if(tmp>ans) ans=tmp,p=i;
     93         else if(tmp==ans) p=i;
     94     }
     95     g[mid][0]=ans;
     96     if(ans==-inf)
     97     {
     98         for(i=mid+1;i<=r1;i++) if(i>=(S-r2)){ solve_g0(i,r1,l2,r2); break; }
     99         return;
    100     }
    101     solve_g0(l1,mid-1,p,r2); solve_g0(mid+1,r1,l2,p);
    102 }
    103 void solve_g1(int l1,int r1,int l2,int r2)
    104 {
    105     if(l1>r1||l2>r2) return;
    106     int mid=(l1+r1)>>1,i,p=-1; ll tmp,ans=-inf;
    107     for(i=r2;i>=l2&&2*(S-i)<=mid;i--)
    108     {
    109         tmp=s.query(mid-2*(S-i),1,ma,s.root[i-1],s.root[S-1]);
    110         if(tmp>ans) ans=tmp,p=i;
    111         else if(tmp==ans) p=i;
    112     }
    113     g[mid][1]=ans;
    114     if(ans==-inf)
    115     {
    116         for(i=mid+1;i<=r1;i++) if(i>=2*(S-r2)){ solve_g1(i,r1,l2,r2); break; }
    117         return;
    118     }
    119     solve_g1(l1,mid-1,p,r2); solve_g1(mid+1,r1,l2,p);
    120 }
    121  
    122 int main()
    123 {
    124     scanf("%d%d%d",&n,&S,&D); S++;
    125     int i; ll ans=0;
    126     for(i=1;i<=n;i++) a[i]=gint(),b[i]=a[i];
    127     sort(b+1,b+n+1); ma=unique(b+1,b+n+1)-(b+1);
    128     for(i=1;i<=n;i++)
    129     {
    130         a[i]=lower_bound(b+1,b+ma+1,a[i])-b;
    131         s.update(a[i],1,ma,s.root[i-1],s.root[i],b[a[i]]);
    132     }
    133     solve_f0(1,D,S,n); solve_f1(1,D,S,n); 
    134     solve_g0(1,D,1,S-1); solve_g1(1,D,1,S-1);
    135     for(i=0;i<=D;i++){ans=max(ans,max( max(f[i][0],g[i][0]) , max(f[i][1]+g[D-i][0],f[i][0]+g[D-i][1]) )); }
    136     printf("%lld
    ",ans);
    137     return 0;
    138 }
  • 相关阅读:
    实习笔记day03
    实习笔记day02
    实习笔记day01
    第4章:数组与方法
    栈内存与堆内存的区别
    java数据类型
    保护模式指令
    空描述符
    段描述符
    全局描述符表
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10258598.html
Copyright © 2011-2022 走看看