• luogu4182 [USACO18JAN] Lifeguards P (单调队列优化dp)


    显然可以先把被覆盖掉的区间去掉,然后排个序,左、右端点就都是单调的

    设f[i][j]表示前i个区间中删掉j个,而且钦定i不能删的最大覆盖长度

    (如果不钦定,就要有一个删掉的状态,那我无法确定前面的到底到哪是没删的)

    那么有$f[i][j]=max{f[k][j-(i-k-1)]+R[i]-max(L[i],R[k])} ,k<i$

    稍微理解一下:k是我们下一次钦定要选的,中间的区间就都扔掉,然后有两种情况:k和i重合或不重合,那新增加一个i区间多覆盖的长度就要从i的左端点和k的右端点挑一个较大的减一减

    复杂度$O(nk^2)$,显然需要优化

    现在我们考虑f[i][j]到底能从哪些状态转移过来

    设能转移到f[i][j]的状态是f[k][l],然后根据上面的式子,我们发现k-l=i-j-1 ,就是说当i和j固定时,l只和k有关

    而且在这些状态中,对于一些比较小的k,它是和i区间不相交的,也就是它的贡献只和它的f有关

    那我们就在这些k中先取一个最大的f[k][l],然后加上i区间的长度,作为一个可选的答案

    对于剩下那些k,它的贡献就变成了f[k][l]-R[k],也是和i无关的

    那我们就可以用一些单调队列,每个单调队列q[i]维护k-l=i的k,l的最大贡献

    我们每次求f[i][j]的时候,先把q[i-j-1]中队头表示的区间不与i相交的踢出去,然后这个要求的最大值就是队头,然后再把f[i][j]加到q[i-j]里就完事了

    为了方便统计答案,我们增加一个从1e9到1e9的区间,然后钦定它要选就可以了

     1 #include<bits/stdc++.h>
     2 #define pa pair<int,int>
     3 #define ll long long
     4 using namespace std;
     5 const int maxn=100010,maxk=110;
     6 
     7 inline ll rd(){
     8     ll x=0;char c=getchar();int neg=1;
     9     while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();}
    10     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    11     return x*neg;
    12 }
    13 
    14 struct POS{
    15     int l,r;
    16 }pos[maxn],p[maxn];
    17 int N,K;
    18 int f[maxn][maxk];
    19 int que[maxn][maxk],qh[maxn],qt[maxn],ma[maxn];
    20 
    21 inline bool cmp(POS a,POS b){
    22     return a.l==b.l?a.r>b.r:a.l<b.l;
    23 }
    24 
    25 int main(){
    26     int i,j,k;
    27     //freopen("testdata.in","r",stdin);
    28     N=rd(),K=rd();
    29     for(i=1;i<=N;i++){
    30         pos[i].l=rd(),pos[i].r=rd();
    31     }sort(pos+1,pos+N+1,cmp);
    32     int mm=0;
    33     for(i=1,j=0;i<=N;i++){
    34         if(mm<pos[i].r) p[++j]=pos[i],mm=pos[i].r;
    35     }K-=N-j;N=j;
    36     if(K<=0){
    37         int ans=0;
    38         for(i=1;i<=N;i++){
    39             ans+=max(0,p[i].r-max(p[i-1].r,p[i].l));
    40         }printf("%d
    ",ans);
    41     }else{
    42         p[N+1].l=1e9,p[N+1].r=1e9;N++;
    43         for(i=1;i<=N;i++){
    44             for(j=0;j<=min(i-1,K);j++){
    45                 int ii=i-j-1;
    46                 while(qh[ii]<qt[ii]&&p[que[ii][qh[ii]]].r<=p[i].l){
    47                     ma[ii]=max(ma[ii],f[que[ii][qh[ii]]][que[ii][qh[ii]]-ii]);qh[ii]++;
    48                 }int hh=que[ii][qh[ii]];
    49                 f[i][j]=max(ma[ii]+p[i].r-p[i].l,f[hh][hh-ii]+p[i].r-max(p[hh].r,p[i].l));
    50                 ii=i-j;int qq=que[ii][qt[ii]];
    51                 while(qh[ii]<qt[ii]&&f[qq][qq-ii]-p[qq].r<=f[i][j]-p[i].r){
    52                     qt[ii]--;qq=que[ii][qt[ii]];
    53                 }que[ii][++qt[ii]]=i;if(!qh[ii]) qh[ii]++;
    54             }
    55         }
    56         printf("%d
    ",f[N][K]);
    57     }
    58     return 0;
    59 }
  • 相关阅读:
    Golang 爬虫02
    Golang使用正则
    gin框架对接快递100 查询快递跟踪记录 Golang实现快递查询
    Jetbrains系列产品2019.3.4最新激活方法[持续更新]
    Linux下安装Fiddler
    Golang 爬虫01
    Github进行fork后如何与原仓库同步
    Pr 的导出视频
    Linux-平均负载指数
    Linux-进程管理命令
  • 原文地址:https://www.cnblogs.com/Ressed/p/9677916.html
Copyright © 2020-2023  润新知