LOJ2331 某位歌姬的故事 和 CF1327F AND Segments
LOJ2331 某位歌姬的故事
IA 是一名会唱歌的女孩子。
IOI2018 就要来了,IA 决定给参赛选手们写一首歌,以表达美好的祝愿。这首歌一共有 \(n\) 个音符,第 \(i\) 个音符的音高为 \(h_i\)。IA 的音域是 \(A\),她只能唱出 \(1\sim A\) 中的正整数音高。因此 \(1\le h_i\le A\)。
在写歌之前,IA 需要确定下这首歌的结构,于是她写下了 \(Q\) 条限制,其中第 \(i\) 条为:编号在 \(l_i\) 到 \(r_i\) 之间的音符的最高音高为 \(m_i\)。在确定了结构之后,她就可以开始写歌了。不过她还是想知道,一共有多少种可能的歌曲满足她的所有限制?她听说你还有 9 个月就要去 IOI 了,于是希望你帮她计算一下这个值。
\(Q\leq 500\)。
题解
仓鼠《动态规划选讲》。
\(max_{l≤i≤r}\{A_i\} = h\)等价于\(∀l ≤ i ≤ r, A_i ≤ h\)且\(∃l ≤ i ≤ r, A_i = h\)。
求出每个点的上限\(up_i\)。可以发现,\(∃l ≤ i ≤ r, A_i = h\)的\(i\)只能由上限\(up_i = h\)的点来贡献
所以对于每个\(h\)的限制以及上限为\(h\)的点单独拿出来DP,满足每个限制区间中至少有一个点达到上限即可。
时间复杂度\(O(Q^3)\),可以利用前缀和优化到\(O(Q^2)\)。
CO int N=1e3+10;
int m,n,A;
vector<int> num,val;
struct node {int l,r,m;} a[N];
int len[N],lim[N];
int t[N],l[N],f[N][N];
int solve(int x){
int tot=0;
for(int i=1;i<(int)num.size();++i)if(lim[i]==x){
t[++tot]=i,l[tot]=0;
for(int j=0;j<=tot;++j) f[tot][j]=0;
}
for(int i=1;i<=n;++i)if(a[i].m==x){
if(!tot) return 0;
int L=lower_bound(t+1,t+tot+1,a[i].l)-t;
int R=lower_bound(t+1,t+tot+1,a[i].r)-t-1;
l[R]=max(l[R],L);
}
f[0][0]=1;
for(int i=1;i<=tot;++i){
int v1=fpow(x,len[t[i]]),v2=fpow(x-1,len[t[i]]);
for(int j=0;j<i;++j)if(f[i-1][j]){
if(j>=l[i]) f[i][j]=add(f[i][j],mul(f[i-1][j],v2));
f[i][i]=add(f[i][i],mul(f[i-1][j],add(v1,mod-v2)));
}
}
int ans=0;
for(int i=0;i<=tot;++i) ans=add(ans,f[tot][i]);
return ans;
}
void real_main(){
read(m),read(n),read(A);
num={1,m+1},val.clear();
for(int i=1;i<=n;++i){
read(a[i].l),read(a[i].r),read(a[i].m);
num.push_back(a[i].l),num.push_back(++a[i].r);
val.push_back(a[i].m);
}
sort(num.begin(),num.end());
num.erase(unique(num.begin(),num.end()),num.end());
sort(val.begin(),val.end());
val.erase(unique(val.begin(),val.end()),val.end());
for(int i=1;i<(int)num.size();++i) len[i]=num[i]-num[i-1];
for(int i=1;i<=n;++i){
a[i].l=lower_bound(num.begin(),num.end(),a[i].l)-num.begin()+1;
a[i].r=lower_bound(num.begin(),num.end(),a[i].r)-num.begin()+1;
}
for(int i=1;i<(int)num.size();++i) lim[i]=A+1;
for(int i=1;i<=n;++i)
for(int j=a[i].l;j<a[i].r;++j)
lim[j]=min(lim[j],a[i].m);
int ans=1;
for(int i=1;i<=(int)val.size();++i){
ans=mul(ans,solve(val[i-1]));
if(ans==0) {puts("0"); return;}
}
for(int i=1;i<(int)num.size();++i)
if(lim[i]==A+1) ans=mul(ans,fpow(A,len[i]));
printf("%d\n",ans);
}
int main(){
for(int T=read<int>();T--;) real_main();
return 0;
}
CF1327F AND Segments
You are given three integers \(n, k, m\) and \(m\) conditions \((l_1, r_1, x_1), (l_2, r_2, x_2), \dots, (l_m, r_m, x_m)\).
Calculate the number of distinct arrays \(a\), consisting of \(n\) integers such that:
-
\(0 \le a_i < 2^k\) for each \(1 \le i \le n\);
-
bitwise AND of numbers \(a[l_i] \& a[l_i + 1] \& \dots \& a[r_i] = x_i\) for each \(1 \le i \le m\).
Two arrays \(a\) and \(b\) are considered different if there exists such a position \(i\) that \(a_i \neq b_i\).
The number can be pretty large so print it modulo \(998244353\).
\(1 \le n \le 5 \cdot 10^5, 1 \le k \le 30, 0 \le m \le 5 \cdot 10^5\)
题解
显然每位独立,分开计算,用乘法原理合并。
如果\(x_i=1\),那么毫无疑问区间内所有数都是\(1\)。
如果\(x_i=0\),那么区间内至少有一个数为\(0\)。这个限制跟上一道题一模一样。
利用前缀和把DP优化到线性。时间复杂度\(O(n\log n)\)。
CO int N=5e5+10;
struct node {int l,r,x;} a[N];
int V[N],L[N],S[N];
int solve(int n){
fill(S+1,S+n+1,0),S[0]=1;
int lim=-1;
for(int i=1;i<=n;++i){
if(!V[i]){
int sum=add(S[i-1],mod-(lim>=0?S[lim]:0));
lim=max(lim,L[i]-1);
S[i]=add(S[i-1],sum);
}
else S[i]=S[i-1];
}
return add(S[n],mod-(lim>=0?S[lim]:0));
}
int fa[N];
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main(){
int n=read<int>(),K=read<int>(),m=read<int>();
for(int i=1;i<=m;++i) read(a[i].l),read(a[i].r),read(a[i].x);
int ans=1;
for(int c=0;c<K;++c){
for(int i=1;i<=n;++i) fa[i]=i;
fill(V+1,V+n+1,0);
for(int i=1;i<=m;++i)if(a[i].x>>c&1)
for(int p=find(a[i].r);p>=a[i].l;p=find(p))
V[p]=1,fa[p]=p-1;
fill(L+1,L+n+1,0);
bool flag=0;
for(int i=1;i<=m;++i)if(~a[i].x>>c&1){
int p=find(a[i].r);
if(!p) {flag=1;break;}
L[p]=max(L[p],a[i].l);
}
if(flag) {ans=0;break;}
ans=mul(ans,solve(n));
}
printf("%d\n",ans);
return 0;
}