CF1313D
题目
给定 和 个区间 。
对于一个长为 的初始值为 的序列 ,在给定的 个区间中选择若干个,使序列 在区间 之间的值 ,其中保证任何时刻序列 中最大值小于 。
求问序列中最多奇数个数。
。
题解
思路:
首先注意到 的取值范围,可以想到要用状压。
对于数据范围 ,显然 的时间复杂度无法接受。
可以发现, 个序列中不同的点最多只有 个,所以进行离散化。
奇偶数之间的转化可以用异或实现。
对于状态 ,可以通过 __builtin_parity()
函数快速求得其中有奇数个还是偶数个 ,返回值为 则个数为奇数个,这在 DP 转移时可以用到。
预处理:
应用差分的思想,对于一个区间 ,将其拆成 和 ,分成开始目前区间和结束目前区间两种操作。
具体实现可以用 STL: vector <pair <int,int> >
,分别存入 和 ,按序列中位置排序。
DP:
设 表示离散化后序列第 个位置的状态为 时,第 个位置及其之前的序列中最多的奇数个数,初始值为极小值。
设 表示在序列第 个位置时,第 个对其有影响的区间(没有就是 )。
- 如果当前操作是开始某个区间
遍历数组 ,找到第一个 的位置 ,此时 即当前区间是第 个对位置 有影响的区间。- 如果状态 的第 位是 ,即状态 下加入了当前区间,则
f[i][j]
应由f[i-1][j^(1<<p)]
转移而来。 - 如果状态 的第 位是 ,即状态 下未加入当前区间,则
f[i][j]
应由f[i-1][j]
转移而来。
- 如果状态 的第 位是 ,即状态 下加入了当前区间,则
对于开始某个区间的转移,其中都要先记录下离散化后当前位置与下一个位置之间的长度 (最后一个位置为 ),转移时加上 len*__builtin_parity(j)
,即当前状态下增加的奇数个数。
- 如果当前操作是结束某个区间
遍历数组 ,找到当前结束的区间的位置 并清空 ,具体原因可以结合前文理解。- 如果状态 的第 位是 ,即状态 下依然存在当前区间,可知状态 不合法,所以赋极小值。
- 如果状态 的第 位是 ,即状态 下已不存在当前区间,则
f[i][j]
应由两个合法状态f[i-1][j]
和f[i-1][j^(1<<p)]
中的较大值转移而来。
因为当前状态的前一步状态可以本来就没有当前区间即f[i-1][j]
,也可以是在位置 时去掉了当前区间即f[i-1][j^(1<<p)]
。所以取较大值转移。
对于结束某个区间的转移,如果情况合法,也要在转移时加上 len*__builtin_parity(j)
。
优化:
此时的空间复杂度达到了 ,所以要对空间进行优化。
- 可以发现,对于开始某个区间的 DP 转移,在
f[i][j]
由f[i-1][j^(1<<p)]
转移而来的情况下,j
一定大于j^(1<<p)
。 - 可以发现,对于结束某个区间的 DP 转移,在
f[i][j]
由f[i-1][j]
和f[i-1][j^(1<<p)]
中的较大值转移而来的情况下,j
一定小于j^(1<<p)
。
所以可以将数组 的第一维省略掉,即设 为状态为 时的当前位置前的奇数最大值,并对 DP 的转移略为改动:
- 如果操作是开始区间,就逆向遍历所有状态:
for(int i=(1<<k)-1;i>=0;i--)
,转移时也直接去掉第一维即可。 - 如果操作是结束区间,就正向遍历所有状态,可以类比开始区间的操作。
最后的输出值是 ,也就是序列离散化后最后一个位置在不受任何区间影响时的值。
时间复杂度 ,空间复杂度
完结。
代码
#include <bits/stdc++.h>
#define pb push_back
using namespace std;int rd(){
int w=0,v=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')v=-1;c=getchar();}
while(c>='0'&&c<='9'){w=(w<<1)+(w<<3)+(c&15);c=getchar();}return w*v;
}const int N=8;int n,k,f[1<<N],g[N];vector <pair <int,int> > a;
signed main(){
n=rd(),rd(),k=rd();for(int i=1,l,r;i<=n;i++)l=rd(),r=rd(),a.pb({l,i}),a.pb({r+1,-i});
sort(a.begin(),a.end());for(int i=1;i<(1<<k);i++)f[i]=-0x7f;
for(int v=0,l,b,p;v<a.size();v++){
b=a[v].second;if(v==a.size()-1)l=0;else l=a[v+1].first-a[v].first;
if(b>0){
for(int i=0;i<k;i++)if(!g[i]){g[p=i]=b;break;}
for(int i=(1<<k)-1;i>=0;i--)
if(i&(1<<p))f[i]=f[i^(1<<p)]+l*__builtin_parity(i);
else f[i]+=l*__builtin_parity(i);
}else{
for(int i=0;i<k;i++)if(g[i]==-b){g[p=i]=0;break;}
for(int i=0;i<(1<<k);i++)
if(i&(1<<p))f[i]=-0x7f;
else f[i]=max(f[i],f[i^(1<<p)])+l*__builtin_parity(i);
}
}cout<<f[0]<<endl;return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)