Codeforces Round #622 (Div. 2) D.Happy New Year(状压dp)

题目

n(n<=1e5)个区间,第i个区间[li,ri](1<=li<=ri<=m,1<=m<=1e9),

保证区间内的每个端点不会被覆盖超过8次,选一些区间使得被覆盖的次数为奇数的点最多

输出最大的被覆盖的次数为奇数的点的个数

思路来源

https://codeforces.com/contest/1313/submission/71864317

https://www.bilibili.com/video/BV1Q7411M7S9?p=5

题解

首先离散化,压成2e5个左开右闭的区间,每个新区间单独考虑,

dp[i][S]表示第i个新区间,8个线段覆盖其的状态为S时,只考虑<=i的新区间时的最大点数

当前x区间选的线段可能和x-1区间选的线段有公有的部分,

如果有一些线段同时覆盖了x-1和x的话,就要么都取,要么都不取

那dp[i][S]表示到第i个区间,第i个区间覆盖状态是S的时候,

如果S包含的x和x-1的公有状态是T,dp[i][S]只能从dp[i-1][T的超集]转移而来,

先把T的超集的答案缩到T更新T,再把S缩到T,用T更新S,思路大致如此

 

但是用tmp数组做跳板的时候,二者下标不一致,即公有状态T在seg[i-1]的下标是j,在seg[i]的下标是k,

这里就新开两个vector,使其pre[l]为seg[i-1]中的j,now[l]为seg[i]中的k,

这样T的超集中出现了第j位的时候,对tmp的第l位赋值,

S出现了第k位的时候,就从tmp含l的位进行转移,这样下标就一致了

 

至于考虑x的时候,为什么不用考虑x-2及更小的区间与x的一致性,

是x-1这个区间已经和前面一致了,所以x和x-1保持一致就可以了,

类似扫描线,不可能断的

代码

#include
using namespace std;
#define pb push_back
const int N=1e5+10,M=2e5+10,S=1<<8;
int n,m,k,l,r,x[M],c;
int dp[M][S],tmp[S],bit[S],ans;
vectorseg[M];
struct line{
    int l,r;
}e[N];
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i>1]+(i&1))%2;
    }
    for(int i=1;i<=n;++i){
        scanf("%d%d",&l,&r);
        r++;
        e[i]={l,r};
        x[++c]=l;x[++c]=r;
    }
    sort(x+1,x+c+1);
    c=unique(x+1,x+c+1)-(x+1);
    for(int i=1;i<=n;++i){
        e[i].l=lower_bound(x+1,x+c+1,e[i].l)-x;
        e[i].r=lower_bound(x+1,x+c+1,e[i].r)-x;
        for(int k=e[i].l;kpre,now;
        for(int j=0;j>pre[k]&1){
                    st|=(1<>now[k]&1){
                    st|=(1<

 

你可能感兴趣的:(#,状压dp/子集dp,状压dp,扫描线)