【学习笔记】CF1456E XOR-ranges

有点难😅

考虑每个数被解除上下界限制时对应的二进制位数

第一次解除限制,一定是二进制中 bit ( L i , p ) ≠ bit ( R i , p ) \text{bit}(L_i,p)\ne \text{bit}(R_i,p) bit(Li,p)=bit(Ri,p)的最高的数位

第二次解除限制,可以是之后的任意一位,发现无论是 L i L_i Li还是 R i R_i Ri,其对应的 在这之前的每一位都是固定的,在这之后的每一位都是自由的,因此考虑以此时对应的低位二进制位数为权值建立小根堆笛卡尔树。

考虑在笛卡尔树上 D P DP DP。发现可以设 d p l , r , k , 0 / 1 , 0 / 1 , 0 / 1 , 0 / 1 , 0 / 1 dp_{l,r,k,0/1,0/1,0/1,0/1,0/1} dpl,r,k,0/1,0/1,0/1,0/1,0/1表示只考虑 ≥ k \ge k k的数位,区间 [ l , r ] [l,r] [l,r] a l , a r a_l,a_r al,ar的值固定,分别等于 L / R L/R L/R,最低位是否翻转,以及这个区间是否存在自由数位的状态。

转移有两种:
1.1 1.1 1.1 枚举 [ l + 1 , r − 1 ] [l+1,r-1] [l+1,r1]中的位置 i i i,表示将 a i a_i ai固定为 L / R L/R L/R,注意如果存在自由数位那么强制最低位要取反,并且最低位之前的 L i ≠ R i L_i\ne R_i Li=Ri。然后将区间划分成 [ l , i ] [l,i] [l,i] [ i , r ] [i,r] [i,r]两个子区间

1.2 1.2 1.2 [ l , r ] [l,r] [l,r]中的每个数的最低位都是自由的,那么让数位增加 1 1 1,并且计算最低数位的贡献

复杂度 O ( n 3 K ) O(n^3K) O(n3K)

实现的有点丑陋。不知道其他人是怎么实现的。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fi first
#define se second
#define db double
#define ull unsigned long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
int n,K;
ll L[55],R[55],c[55],dp[55][55][55][2][2][2][2][2];
void add(ll &x,ll y){
    x=min(x,y);
}
ll get(ll x,int y){
    return (x>>y)<<y;
}
int calc(int i,int k,int x){
    if(x==0)return L[i]>>k&1;
    return R[i]>>k&1;
}
ll solve(int l,int r,int k,int x,int y,int f1,int f2,int f3){
    if(l==r)return 0;
    if(k==K){
        if(l+1==r)return 0;
        return inf;
    }
    ll &res=dp[l][r][k][x][y][f1][f2][f3];
    if(~res)return res;res=inf;
    if(l+1==r){
        int v1=calc(l,k,x)^f1,v2=calc(r,k,y)^f2;ll v3=(v1^v2)*c[k];
        if(l==0||r==n+1)v3=0;
        add(res,solve(l,r,k+1,x,y,0,0,f3)+v3);
    }
    else{
        for(int i=l+1;i<r;i++){
            if(f3){
                if(get(L[i],k+1)!=get(R[i],k+1)){
                    if(!(L[i]>>k&1))add(res,solve(l,i,k,x,0,f1,1,1)+solve(i,r,k,0,y,1,f2,1));
                    if(R[i]>>k&1)add(res,solve(l,i,k,x,1,f1,1,1)+solve(i,r,k,1,y,1,f2,1));
                }
            }
            else{
                if(get(L[i],k+1)!=get(R[i],k+1)){
                    if(!(L[i]>>k&1))add(res,solve(l,i,k,x,0,f1,1,0)+solve(i,r,k,0,y,1,f2,0));
                    if(R[i]>>k&1)add(res,solve(l,i,k,x,1,f1,1,0)+solve(i,r,k,1,y,1,f2,0));
                }
                add(res,solve(l,i,k,x,0,f1,0,0)+solve(i,r,k,0,y,0,f2,0));
                add(res,solve(l,i,k,x,1,f1,0,0)+solve(i,r,k,1,y,0,f2,0));
            }
        }int v1=calc(l,k,x)^f1,v2=calc(r,k,y)^f2;ll v3=0,v4=0;
        if(l!=0)v3+=v1*c[k],v4+=(v1^1)*c[k];if(r!=n+1)v3+=v2*c[k],v4+=(v2^1)*c[k];
        add(res,solve(l,r,k+1,x,y,0,0,1)+min(v3,v4));
    }
    return res;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>K;for(int i=1;i<=n;i++)cin>>L[i]>>R[i];
    for(int i=0;i<K;i++)cin>>c[i];
    memset(dp,-1,sizeof dp);
    cout<<solve(0,n+1,0,0,0,0,0,0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值