AT_joisc2015_f题解

· · 题解

题目传送门。

思路

我们称一个员工的进出为一个事件。

我们不妨将所有事件按照事件发生的时间点排序,我们假设 i 为触发一个事件的员工,j 为下一个时间点触发事件的员工,考虑每种情况对答案(也就是大门锁的时间)什么时候有贡献,我们可以分成 4 类来讨论:

这样我们就转换成了一个图上问题,不难发现这个图是由若干条链组成的(因为每个员工只有两种状态),我们不妨将这些链组成一个大链,对这个链式序列 DP 即可。

记 val_i 表示 i 点权,val1_i 表示 i 到连接到的那个点的边权。

我们设 f_{i,j,0/1} 表示当前链的前面 i 个人,共有 j 把钥匙,第 i 个人是否有钥匙的最大贡献。

若第 i 个人没有钥匙,那么有 f_{i,j,0}=\max\{f_{i,j,0},f_{i-1,j,1},f_{i-1,j,0}\}。

若第 i 个人有钥匙,那么有 f_{i,j+1,1}=\max\{f_{i,j+1,1},f_{i-1,j,0}+val_i,f_{i-1,j,1}+val_i+val1_i\}

(这个边两端点都有 1,因此两点之间有边,所以要加上边权)

```cpp #include <bits/stdc++.h> #define ll long long using namespace std; const int Maxn=2010; int f[2][Maxn][2]; struct node{ int pos,id; bool flg; }a[Maxn<<2]; int n,m,k,tot,nxt[Maxn],val[Maxn],val1[Maxn]; int main(){ scanf("%d%d%d",&n,&m,&k); for(int i=1,l,r;i<=n;i++){ scanf("%d%d",&l,&r); a[++tot]=(node){l,i,0}; a[++tot]=(node){r,i,1}; } sort(a+1,a+tot+1,[](const node x,const node y){return x.pos<y.pos;}); f[0][0][0]=(a[1].pos+m-a[tot].pos); for(int i=1;i<tot;i++){ int w=a[i+1].pos-a[i].pos; if(a[i].flg&&!a[i+1].flg) f[0][0][0]+=w; if(a[i].flg&&a[i+1].flg) val[a[i+1].id]+=w; if(!a[i].flg&&!a[i+1].flg) val[a[i].id]+=w; if(!a[i].flg&&a[i+1].flg){ if(a[i+1].id==a[i].id) val[a[i].id]+=w; else nxt[a[i+1].id]=a[i].id,val1[a[i].id]+=w; } } int pre=0; for(int i=1;i<=n;i++) if(!val1[i]){ nxt[pre]=i; while(nxt[pre]) pre=nxt[pre]; } int tp=0; for(int i=nxt[0];i;i=nxt[i]){ tp^=1; for(int j=0;j<=k;j++){ for(int h=0;h<=1;h++){ if(!f[tp^1][j][h]) continue; f[tp][j][0]=max(f[tp][j][0],f[tp^1][j][h]); f[tp][j+1][1]=max(f[tp][j+1][1],f[tp^1][j][h]+val1[i]*h+val[i]); f[tp^1][j][h]=0; } } } printf("%d\n",max(f[tp][k][0],f[tp][k][1])); return 0; } ```