【题解】 CF1327F AND Segments dp+按位

Legend

Link \(\textrm{to Codeforces}\)

现有一个长 \(n\) 的数组 \(a\ (0 \le a < 2^k)\),给定 \(m\) 个限制,形如 \(l,r,x\) 表示 \(\operatorname{AND}\limits_{i=l}^{r} a_i=x\)

求出有多少个满足条件的数组。

\(1 \le n \le 5\cdot 10^5\)\(0 \le m \le 5\cdot 10^5\)\(1 \le k \le 30\)

Editorial

容易发现位之间答案独立,最后只需要乘起来,问题转化为 \(a_i,x\in\{0,1\}\)

对于 \(x=1\) 的限制,等价于直接确定了这个区间一定都是 \(1\)

对于 \(x=0\) 的限制,说明这个区间至少有一个 \(0\)

只有 \(x=0\) 的限制时,是个经典题。

做法是设 \(dp_i\) 表示只考虑前 \(i\) 个位置,第 \(i\) 个位置放了 \(0\),使得区间左端点 \(\le i\) 的区间全部满足要求的方案数量。

转移就直接找可行的转移点(右端点 \(<i\) 的区间的最靠右的左端点以后的位置都可行),一定是连续的区间 \(\sum\) 一下就行了。

前缀和可以动态维护,做到 \(O(1)\) 转移。

在此处有了 \(x=1\) 的限制则直接钦定这些地方的 \(dp\) 值为 \(0\) 即可。

复杂度 \(O(k(n+m))\)

Code

#include <bits/stdc++.h>

#define LL long long

using namespace std;

const LL MOD = 998244353;
const int MX = 5e5 + 233;

int read(){
	char k = getchar(); int x = 0;
	while(k < '0' || k > '9') k = getchar();
	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
	return x;
}

int n ,k ,m;
struct segment{
	int l ,r ,x;
	bool operator <(const segment &B)const{
		return r == B.r ? l > B.l : r < B.r;
	}
}s[MX];

LL ban[MX] ,S[MX] ,dp[MX];
LL solve(int bit){
	for(int i = 1 ; i <= n + 1 ; ++i) ban[i] = 0;
	for(int i = 1 ; i <= m ; ++i){
		if((s[i].x >> bit) & 1){
			ban[s[i].l]++;
			ban[s[i].r + 1]--;
		}
	}
	int sl = 1 ,lim = 0;
	dp[0] = S[0] = 1;

	for(int i = 1 ; i <= n + 1 ; ++i){
		ban[i] += ban[i - 1];
		
		while(sl <= m && s[sl].r < i){
			if(((s[sl].x >> bit) & 1) == 0){
				lim = max(lim ,s[sl].l);
			}
			++sl;
		}

		if(ban[i]) dp[i] = 0;
		else if(lim) dp[i] = (S[i - 1] - S[lim - 1] + MOD) % MOD;
		else dp[i] = S[i - 1];
		S[i] = (S[i - 1] + dp[i]) % MOD;
	}
	return dp[n + 1];
}

int main(){
	n = read() ,k = read() ,m = read();
	for(int i = 1 ; i <= m ; ++i){
		s[i].l = read() ,s[i].r = read() ,s[i].x = read();
	}
	LL Ans = 1;
	sort(s + 1 ,s + 1 + m);
	for(int i = 0 ; i < k ; ++i){
		Ans = Ans * solve(i) % MOD;
	}
	cout << Ans << endl;
	return 0;
}
posted @ 2020-09-26 11:08  Imakf  阅读(125)  评论(0编辑  收藏  举报