题解 CF1313D Happy New Year
带有小 trick 的 DP,长知识了。
很大,需要离散化。
为了方便,采用扫描线的方式,不对其进行实际意义上的离散,而是对于第 个区间 ,插入 两个 pair,最后排个序。这样相邻两个 pair 之间的部分就缩成了一个点。
同时我们还发现 ,也就是说经过每个点的区间个数不超过 。于是在遍历每一个点的时候,我们记录当前有哪些区间经过它,并给它们编号(小于等于 )。
定义 表示已经遍历到第 个点,经过点 的所有区间中被选的状态为 ,最大的快乐值。
定义 表示集合 中有奇数个元素还是偶数个元素。如果是奇数个,返回 ;否则返回 。
定义 表示当前点对应的实际长度。
转移分两类讨论。
-
新遍历到了一个区间:
-
选:
-
不选:
-
-
遍历到了一个区间的结尾,需要删除:
-
不删(不符合条件):
-
删:
-
复杂度 ,实际上达不到。
记得滚动数组。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,inf=INT_MAX>>1;
int n,m,k,vis[10],f[N];
pair<int,int>a[N*2];
inline int chk(int x){
int cnt=0;
while(x){x-=x&(-x);++cnt;}
return cnt&1;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;++i){
int l,r;cin>>l>>r;
a[i]=make_pair(l,i);a[i+n]=make_pair(r+1,-i);
}
sort(a+1,a+2*n+1);
for(int i=1;i<=256;++i)f[i]=-inf;
for(int i=1;i<=2*n;++i){
int t=a[i].second,k,len=(i==2*n?0:a[i+1].first-a[i].first);
if(t>0){
for(int j=0;j<8;++j)if(!vis[j]){vis[j]=t;k=j;break;}
for(int j=255;j;--j){
if((j>>k)&1)f[j]=f[j-(1<<k)]+len*chk(j);
else f[j]+=len*chk(j);
}
}else{
for(int j=0;j<8;++j)if(vis[j]==-t){vis[j]=0;k=j;break;}
for(int j=0;j<256;++j){
if((j>>k)&1)f[j]=-inf;
else f[j]=max(f[j],f[j+(1<<k)])+len*chk(j);
}
}
}
cout<<f[0]<<endl;
return 0;
}
本文作者:HQJ2007
本文链接:https://www.cnblogs.com/HQJ2007/p/17561343.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步