Codeforces 1327 F AND Segments —— dp,前缀和,有丶东西

This way

题意:

需要你构造长度为n的序列,要求是,每个数的大小< 2 k 2^k ,并且有m个区间,每个区间
在这里插入图片描述

题解:

那么一般来说可以按位分解的题目是枚举每一位去做的,而且这道题每个位置上的数的每一位都是互不影响的,那么肯定是一位一位去考虑然后乘法原理乘起来。
然后根据题意我们可以知道每个区间,如果要求的数的第i位是1,那么这个区间里面所有的数的第i位一定是1,如果是0,那么这个区间里面所有数的第i位至少有一个数是0.
这道题的数据范围是5e5,之后我就没有再想二维的做法了,但是应该是二维的做法,最终由于这道题的特殊性质可以转换成一维的。
dp[i][j]表示到第i位的时候,最后一个0在第j位的时候的情况数。
状态转移方程就是,

dp[i][j]=dp[i-1][j](0<=j<i)
dp[i][i]=sum{0<=j<i|dp[i-1][j]}

此时可以发现第i位只和地i-1位有关,并且除了dp[i][i],其余的数都是和前面一样的。所以直接去掉第一维,用dp[j]表示即可,然后用sum维护前缀和。
然后的话用差分维护当前这一位是否需要1,如果需要,那么dp[i]=0,否则
dp[i]=sum,sum*=2
*2是因为dp[i-1]就是前缀和,然后再加一个前缀和就是两倍。
然后对于区间的约束,到了当前位置如果区间l-i是要求有一个0,那么需要减去l之前的dp值,因为不存在 d p [ i ] [ j ] ( 0 < = j < l ) dp[i][j](0<=j<l)
That’s all

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=5e5+5;
const ll mod=998244353;
ll dp[N];
vector<int>add[N],del[N];
int lef[N],l[N],r[N],v[N];
int main()
{
    int n,k,m;
    scanf("%d%d%d",&n,&k,&m);
    ll ans=1;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&l[i],&r[i],&v[i]),add[l[i]].push_back(v[i]),del[r[i]+1].push_back(v[i]);
    for(int bit=0;bit<k;bit++){
        memset(lef,0,sizeof(lef));
        for(int i=1;i<=m;i++)
            if(!(v[i]&(1<<bit)))
                lef[r[i]]=max(lef[r[i]],l[i]);
        memset(dp,0,sizeof(dp));
        int num=0,f=0,pos=0;
        ll sum=dp[0]=1;
        for(int i=1;i<=n;i++){
            for(auto j:add[i])  
                num+=(j&(1<<bit))>0;
            for(auto j:del[i])
                num-=(j&(1<<bit))>0;
            if(!num)
                dp[i]=sum,sum=(sum*2)%mod;
            while(pos<lef[i])
                sum=(sum-dp[pos++]+mod)%mod;
        }
        ans=ans*sum%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

发布了584 篇原创文章 · 获赞 33 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/tianyizhicheng/article/details/105316893