【Codeforces】Educational Codeforces Round 137 - F. Intersection and Union

135 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 1 月更文挑战」的第19天,点击查看活动详情

【Codeforces】Educational Codeforces Round 137 - F. Intersection and Union

题目链接

Problem - F - Codeforces

题目大意

给定 n 个区间[li,ri] (2n300000,0liri300000)[l_i,r_i] (2≤n≤300000,0≤l_i≤r_i≤300000)SiS_i 代表第 ii 个区间中的整数点集。

对这 nn 个点集做如下操作:

((...(((S1 op1 ⁡S2)op2 ⁡S3)op3 ⁡S4)...)opn1 Sn)((...(((S_1\ op_1\ ⁡S_2)op_2\ ⁡S_3)op_3\ ⁡S_4)...)op_{n-1⁡}\ S_n)

其中 opiop_i 为交,并,对称差之一。

这 n1n−1 个操作别取三种操作之一,总共得到的 3n13^{n−1} 个集合。输出每个集合中元素个数之和。

思路

假设前 i1i-1 个集合经过某个操作序列得到的集合为 SS

SSiS∪S_iSSiS∩S_iSΔSiSΔS_i 中,讨论元素 xx 的存在性如下:

  • 如果 xS,xSix\in S,x\in S_i,交、并操作后得到的集合含有 xx
  • 如果 xS,xSix\in S,x\notin S_i,并、对称差操作后得到的集合含有 xx
  • 如果 xSi,xSx\in S_i,x\notin S,并、对称差操作后得到的集合含有 xx
  • 如果 xS,xSix\notin S,x\notin S_i,三种操作后得到的集合均不会含有 xx

考虑包含 xx 的最后一个集合 SjS_j,无论前面操作结果是什么,第 j1j−1 个操作之后的每个操作只有两种选择能使得最终的结果里存在 xx

所以我们只需要使用线段树求出非负整数 xx 出现的最后一个区间的序号 jj

  • 如果 xx 没出现过,答案增加 0。
  • 如果 jj 是 1,则对于全部的 n1n-1 个操作,每个操作只有两种选择能使得最终的结果里存在 xx,所以我们让答案增加 2n12^{n-1}
  • 否则,对于前 j2j-2 个操作,我们不关心其取得的结果;后 nj+1n-j+1 个操作,每个操作只有两种选择能使得最终的结果里存在 xx, 所以我们让答案增加 3j2×2nj+13^{j-2}\times 2^{n-j+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;
}