题解 CF1106E Lunar New Year and Red Envelopes

小清新 DP 题。

定义 fi,j 表示在时刻 i,干扰了 j 次,最小贡献。

定义 nexi 表示在时刻 i 会收集哪个红包。

那么转移方程为:

fdnexi+1,j=min(fi,j+wnexi)

fi+1,j+1=min(fi,j)

其中,f1,0=0ans=minj=0mfn+1,jdnexi+1 为选完编号为 wnexi 的红包后会跳到那个位置。

现在考虑如何预处理出 nexi

可以先以 w 为第一关键字,d 为第二关键字从大到小排序。

然后将区间 [s,t] 中的 nex 全部标记为当前编号,之后不再访问。

这里可以用并查集维护已经访问过的区间,遇到访问过的直接查询并查集跳过去即可。

复杂度 O(nm+nlogn)

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e5+5,N2=200+5,inf=LLONG_MAX>>1;
int n,m,k,vis[N],nex[N],fa[N];
ll f[N][N2];
struct node{int s,t,d;ll w;}a[N];
bool cmp(node x,node y){return x.w!=y.w?x.w>y.w:x.d>y.d;}
int ff(int x){return fa[x]==x?x:fa[x]=ff(fa[x]);}
int main(){
  ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  cin>>n>>m>>k;
  for(int i=1;i<=k;++i)cin>>a[i].s>>a[i].t>>a[i].d>>a[i].w;
  sort(a+1,a+k+1,cmp);
  for(int i=1;i<=n+1;++i)fa[i]=i;
  for(int i=1;i<=k;++i){
    int j=a[i].s;
    while(j<=a[i].t){
      if(vis[j])j=ff(j);
      else {
        vis[j]=1;
        nex[j]=i;
        fa[ff(j)]=ff(j+1);
        ++j;
      }
    }
  }
  for(int i=1;i<=n+1;++i)for(int j=0;j<=m;++j)f[i][j]=inf;
  f[1][0]=0;
  for(int i=1;i<=n;++i){
    for(int j=0;j<=m;++j){
      f[i+1][j+1]=min(f[i+1][j+1],f[i][j]);
      if(nex[i])f[a[nex[i]].d+1][j]=min(f[a[nex[i]].d+1][j],f[i][j]+a[nex[i]].w);
      else f[i+1][j]=min(f[i+1][j],f[i][j]);
    }
  }
  ll ans=inf;
  for(int j=0;j<=m;++j)ans=min(ans,f[n+1][j]);
  cout<<ans<<endl;
  return 0;
}

本文作者:HQJ2007

本文链接:https://www.cnblogs.com/HQJ2007/p/17561362.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   HQJ2007  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起