码迷,mamicode.com
首页 > 其他好文 > 详细

[CF1327F]AND Segments

时间:2020-07-20 10:39:00      阅读:56      评论:0      收藏:0      [点我收藏+]

标签:tin   name   二进制   定义   最大的   为什么   memset   pos   lan   

题目

传送门

题解

考虑对于一个区间 \([l,r]\),其并值为 \(x\) 会有什么限制:

  1. 如果 \(x\) 的第 \(i\) 位为 \(1\),则要求 \([l,r]\) 所有数字的第 \(i\) 位为 \(1\)
  2. 如果 \(x\) 的第 \(i\) 位为 \(0\),则要求 \([l,r]\) 的所有数字第 \(i\) 位不全为 \(1\)

由于所有数字的每个二进制位都是独立的,我们可以考虑先将所有数每个二进制位的选择算出来,最后累乘即可得到答案。

考虑每个二进制位进行 \(DP\),令 \(f[i]\) 表示前一个 \(0\) 填在第 \(i\) 个数字上的合法方案。

首先,如果 \(\exist t,i\in [l_t,r_t]_1\) (此处区间下标是指要求的类别),那么 \(f[i]\)\(0\),这个我们可以在初始化时搞定。

其次,考虑如何进行转移,由于我们已经经过初始化,相当于自动满足要求一,只需考虑如何满足要求二,进行分类讨论:

  1. 对于 \([l_t,r_t]_2\),如果 \(r_t\ge i\),由于 \(f[i]\) 的定义,这个要求一定可以被满足(因为我们已经在 \(i\) 填了 \(0\),满足一个区间不全为 \(1\)) ;
  2. 对于 \([l_t,r_t]_2\),如果 \(r_t<i\),那么我们一定要在 \([l_t,r_t]_2\) 中填一个 \(0\),那么 \(f[i]=\sum_{j=l_t}^{j\le i-1}f[j]\),为什么是到 \(i-1\) 而非 \(r_t\) 呢?因为 \(f[j]\) 已经是所谓的“合法方案”,那么对于 \(j\in [r_t+1,i-1]\) 也是一定满足了 \([l_r,r_t]_2\) 这个区间要求的方案数。
  3. 由于我们要满足所有的条件二,那么我们就必须在 \(2\) 中的所有 \(l_t\) 中,找到最大的 \(l_t‘\),然后求 \(\sum_{j=l_t‘}^{i-1}f[j]\),发现这是一个连续区间求和,可以考虑维护一个前缀数组。

对于每一位都这样做,然后累乘 \(f[n]\) 的值即可。

时间复杂度 \(\mathcal O(nk)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

const int MAXN=5e5;
const int MOD=998244353;

struct require{int l,r;
	inline bool operator <(const require rhs)const{
		if(r==rhs.r)return l<rhs.l;
		return r<rhs.r;
	}
};
vector<require>q[35];
vector<require>make[35];

int n,k,m;

inline bool cmp(const require lhs,const require rhs){
	if(lhs.l==rhs.l)return lhs.r<rhs.r;
	return lhs.l<rhs.l;
}

inline void Init(){
	scanf("%d %d %d",&n,&k,&m);
	for(int i=1,l,r,s;i<=m;++i){
		scanf("%d %d %d",&l,&r,&s);
		for(int i=0;i<k;++i){
			if((s>>i)&1)make[i].push_back(require{l,r});
			else q[i].push_back(require{l,r});
		}
	}
	for(int i=0;i<k;++i)
		sort(q[i].begin(),q[i].end()),
		sort(make[i].begin(),make[i].end(),cmp);
}

int f[MAXN+5];
int pre[MAXN+5];

inline int calc(const int t){
	// memset(f,0,sizeof f);
	for(int i=0;i<=n+1;++i)f[i]=0;
	int pos=0;
	for(int i=0,sz=make[t].size();i<sz;++i){
		pos=max(pos,make[t][i].l);
		for(;pos<=make[t][i].r;++pos)f[pos]=-1;
	}

	f[0]=pre[0]=1;
	int p=0,maxl=0,sz=q[t].size();//指向要求的最后一个
	// printf("Now sz == %d\n",sz);
	for(int i=1;i<=n+1;++i){
		pre[i]=pre[i-1];
		if(f[i]==-1)continue;
		while(p<sz && q[t][p].r<i)maxl=max(maxl,q[t][p++].l);
		if(maxl==0)f[i]=pre[i-1];
		else{
			f[i]=pre[i-1]-pre[maxl-1];
			if(f[i]<0)f[i]+=MOD;
		}
		pre[i]+=f[i];
		if(pre[i]>=MOD)pre[i]-=MOD;
		// printf("f[%d] == %d\n",i,f[i]);
	}return f[n+1];
}

int ans=1;

signed main(){
	Init();
	for(int i=0;i<k;++i){
		// printf("calc(%d) == %d\n",i,calc(i));
		ans=1ll*ans*calc(i)%MOD;
	}
	printf("%d\n",ans);
	return 0;
}

[CF1327F]AND Segments

标签:tin   name   二进制   定义   最大的   为什么   memset   pos   lan   

原文地址:https://www.cnblogs.com/Arextre/p/13343142.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!