持续创作,加速成长!这是我参与「掘金日新计划 · 1 月更文挑战」的第19天,点击查看活动详情
【Codeforces】Educational Codeforces Round 137 - F. Intersection and Union
题目链接
题目大意
给定 n 个区间, 代表第 个区间中的整数点集。
对这 个点集做如下操作:
其中 为交,并,对称差之一。
这 个操作别取三种操作之一,总共得到的 个集合。输出每个集合中元素个数之和。
思路
假设前 个集合经过某个操作序列得到的集合为 。
在 , 与 中,讨论元素 的存在性如下:
- 如果 ,交、并操作后得到的集合含有 。
- 如果 ,并、对称差操作后得到的集合含有 。
- 如果 ,并、对称差操作后得到的集合含有 。
- 如果 ,三种操作后得到的集合均不会含有 。
考虑包含 的最后一个集合 ,无论前面操作结果是什么,第 个操作之后的每个操作只有两种选择能使得最终的结果里存在 。
所以我们只需要使用线段树求出非负整数 出现的最后一个区间的序号 :
- 如果 没出现过,答案增加 0。
- 如果 是 1,则对于全部的 个操作,每个操作只有两种选择能使得最终的结果里存在 ,所以我们让答案增加 。
- 否则,对于前 个操作,我们不关心其取得的结果;后 个操作,每个操作只有两种选择能使得最终的结果里存在 , 所以我们让答案增加 。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=300001;
int s[N<<2],v[N<<2];
int n=300000,m;
const long long mod=998244353;
long long ans,bs[N],bs3[N];
void change(int l,int r,int rt,int x,int y,int val)
{
if (v[rt]&&l!=r) s[rt<<1]=s[rt<<1|1]=v[rt<<1]=v[rt<<1|1]=v[rt];
v[rt]=0;
if (x<=l&&r<=y)
{
s[rt]=v[rt]=val;
return;
}
if (x<=(l+r>>1)) change(l,l+r>>1,rt<<1,x,y,val);
if (y>(l+r>>1)) change((l+r>>1)+1,r,rt<<1|1,x,y,val);
}
int getj(int l,int r,int rt,int x)
{
if (l==r) return s[rt];
if (v[rt]) s[rt<<1]=s[rt<<1|1]=v[rt<<1]=v[rt<<1|1]=v[rt];
v[rt]=0;
if (x<=(l+r>>1)) return getj(l,l+r>>1,rt<<1,x);
return getj((l+r>>1)+1,r,rt<<1|1,x);
}
int solve()
{
scanf("%d",&m);
for (int i=1,x,y;i<=m;++i)
{
scanf("%d%d",&x,&y);
change(0,n,1,x,y,i);
}
ans=0;
for (int i=0;i<=n;++i)
{
int j=getj(0,n,1,i);
if (j==0) continue;
if (j==1) ans+=bs[m-1];
else ans+=bs3[j-2]*bs[m-j+1]%mod;
ans%=mod;
}
printf("%lld\n",ans);
return 0;
}
int main()
{
bs[0]=bs3[0]=1;
for (int i=1;i<=n;++i)
{
bs[i]=bs[i-1]*2%mod;
bs3[i]=bs3[i-1]*3%mod;
}
solve();
return 0;
}