CodeForces 1327F AND Segments
題意
給三個整數 \(n,k,m\) 和 \(m\) 個限制 \((l_i,r_i,x_i)\),求有多少個長度為 \(n\) 的序列 \(a\) 滿足:
-
對於 \(1\leq i\leq n\) 有 \(0\leq a_i<2^k\)
-
對於 \(1\leq i\leq m\) 有 \(a_{l_i} \operatorname{and} a_{l_i+1}\operatorname{and}\cdots\operatorname{and} a_{r_i}=x_i\)
對 \(998244353\) 取模。
\(\texttt{Data Range:}1\leq n\leq 5\times 10^5,1\leq k\leq 30,0\leq m\leq 5\times 10^5\)
題解
毒瘤題。
一個非常顯然的想法是拆位,所以變成每個位置填 \(0\) 或 \(1\) 然後滿足所有條件的限制的方案數,總的方案數就是每一位的方案數乘起來就好了。
如果一段區間限制為 \(1\) 的話那麼所有數都必須填 \(1\),如果限制是 \(0\) 的話那麼至少有一個是 \(0\)。
設 \(f_{i,j}\) 表示當前在位置 \(i\),最後一個 \(0\) 在位置 \(j\) 的方案數,然後你會發現這個東西不好做。
考慮設一個 \(p_i\) 表示 \(i\) 位置前(不包括 \(i\) 位置)第一個 \(0\) 最小能填到哪個位置。
當 \(j<p_i\) 的時候很明顯 \(f_{i,j}=0\)
當 \(p_i\leq j<i\) 的時候,因為 \(i\) 位置沒有填,所以 \(f_{i,j}=f_{i-1,j}\)。
當 \(j=i\) 的時候,如果這個位置強制選 \(1\) 的話那麼 \(f_{i,j}=0\),否則列舉一下上一個 \(0\) 的位置得到 \(f_{i,j}=\sum\limits_{k<j}f_{i-1,k}\)。
注意到 \(i\) 這一維可以滾掉,而 \(p_i\) 又是單調不降的,所以可以考慮用一個指標來維護一下滿足 \(f_{i,j}\neq 0\) 的最小的 \(j\)。
至於第三種操作,因為當 \(i<j\) 的時候 \(f_{i,j}=0\)
然後處理出哪個位置要強制選 \(1\) 的話可以對 \(1\) 的限制涉及到的區間做區間加,可以差分一下再字首和一下。
處理 \(p_i\) 可以考慮每個為 \(0\) 的限制 \((l,r,0)\),記 \(p_{r+1}=l\) 即可。
程式碼
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=5e5+51,MOD=998244353;
ll n,kk,m,res=1,sum,ptr;
ll l[MAXN],r[MAXN],x[MAXN],pos[MAXN],sel[MAXN],f[MAXN];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
inline void calc(ll bit)
{
for(register int i=1;i<=m;i++)
{
if(x[i]&(1<<bit))
{
sel[l[i]]++,sel[r[i]+1]--;
}
else
{
pos[r[i]+1]=max(pos[r[i]+1],l[i]);
}
}
f[0]=sum=1,ptr=0;
for(register int i=2;i<=n+1;i++)
{
sel[i]+=sel[i-1],pos[i]=max(pos[i],pos[i-1]);
}
for(register int i=1;i<=n+1;i++)
{
for(;ptr<pos[i];sum=(sum-f[ptr]+MOD)%MOD,f[ptr++]=0);
f[i]=sel[i]?0:sum,sum=(sum+f[i])%MOD;
}
res=(li)res*f[n+1]%MOD;
for(register int i=0;i<=n+1;i++)
{
sel[i]=pos[i]=f[i]=0;
}
}
int main()
{
n=read(),kk=read(),m=read();
for(register int i=1;i<=m;i++)
{
l[i]=read(),r[i]=read(),x[i]=read();
}
for(register int i=0;i<kk;i++)
{
calc(i);
}
printf("%d\n",res);
}