L - Intersection and Union Gym - 103993L (线段树)
题意
给定 \(n(n\le 3*10^5)\) 个区间$ [l_ir_i] (0\le l_i,r_i \le 3*10^5)$。
每个区间可以看成包含区间内的数的集合 \(S_i\) 。
定义集合的操作有 \(\cap,\cup,\oplus\) 。其中 \(S_a \oplus S_b = S_a \cup S_b - ( S_a \cap S_b)\)
给定一系列的操作符号 \(op_1,op_2,op_3......(op_i\in (\cap\cup\oplus))\)
你需要计算对于 \(op\) 所有的可能下
的和。
其中 \(|S|\) 表示集合的大小(长度)。
也就问:
op可以是交,并,异或,问\(3^{n-1}\)种情况的长度之和是多少。
思路1
按平时的经验,当求好多种方案的总答案的时候,往往是把某个单独的可以贡献答案的东西取出来,单独看它可以对答案做出多少贡献。
发现元素最多一共3e5种,所以完全可以枚举每一种元素对答案的贡献。
我们考虑,每个数 p 会出现的最后的集合
(因为我们计算的是集合的大小)
我们设集合\(S_i\)之前的集合中,有 \(p_1\) 个集合有p,有 \(p_0\) 个集合没有p。
如果 \(S_i\) 中有数字p,
然后我们分别执行三个操作,我们得到:
有p 的集合,经过 \(\cap\cup\) 后还会有p
没有p的集合经过 \(\cup\oplus\) 也会有\(p\)。 (注意 \(S_i\) 中是有\(p\) 的)
如果 \(S_i\) 中没有p,我们可以得到
然后一个想法就出来了,我们用矩阵来进行运算。
思路2
虽然直接矩阵是可以计算的,但是,我们漏了一点,就是进行了i次运算可以得到 \(3^i\) 个集合。
那么 \(p_1 + p_0 = 3^i\)
例如,有个p最后一次出现在S_4中。
则,前面一共有\(3^2\) 个集合,和\(S_4\) 运算后,有\(2*3^2\) 个集合都含有p。
\(p_1 = 2*(p_1+p_0) = 2*3^2\)
因为后面就没有拥有p的集合了。那么后面,每经过一个集合,含有p的集合数目就会乘以2,也
就是
\(2^i * 3^2\) 。
总结一下就是,如果一个数字最后一次出现在集合\(S_w\) 中:
- 前面有 \(3^{w - 2}\) 个集合
- 后续需要经过\(n - w+ 1\) 次乘以\(2\)
\(2^{n - w + 1} * 3^{w - 2}\) 就是最终,含有p这个数字的集合个数。
我们用一个区间赋值保留最大值的线段树进行计算即可。
代码
思路2
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
# define int long long
const int N=3e5+10;
const int mod=998244353;
struct node {
int l,r;
int maxn;
int lazy;
}tr[N*4];
void build(int u,int l,int r){
tr[u]={l,r,0,0};
if(tr[u].l==tr[u].r) return;
int mid=tr[u].l+tr[u].r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
}
void modify(int u,int l,int r,int val){
if(tr[u].l>=l && tr[u].r<=r){
tr[u].maxn=val;
return;
}
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,val);
if(r>mid) modify(u<<1|1,l,r,val);
}
void query(int u,int l ,int &val){//val这里传地址,函数就不用返回最大值了
val=max(val,tr[u].maxn);
//如果没有包含这个l值,结果都是0,只有包含的才不是
if(tr[u].r==tr[u].l) return ;
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) query(u<<1,l,val);
if(l>mid) query(u<<1|1,l,val);
}
int qmi(int a,int b){
int ans =1;
while(b){
if(b&1) ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
signed main()
{
build(1,0,300000);
int n;cin>>n;
for(int i=1;i<=n;i++){
int l,r;
cin>>l>>r;
modify(1,l,r,i);
}
// cout<<"fs"<<endl;
int ans=0;
for(int i=0;i<=300000;i++){
int val=0,sum=0;
query(1,i,val);
if(val==0) continue;//没有出现这个值
if(val==1){//在第1段出现了,后面有n-1个符号
sum=qmi(2,n-1);
}
else{//后面有n-val个符号,前面有3^(val-2)个集合
sum=qmi(2,n-val+1)*qmi(3,val-2)%mod;
sum%=mod;
}
ans+=sum;
ans%=mod;
}
cout<<ans<<endl;
return 0;
}
原文
严格鸽https://zhuanlan.zhihu.com/p/574614229
beyond+myself
https://blog.csdn.net/qq_54783066/article/details/127619409?spm=1001.2014.3001.5501