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\) 所有的可能下
image

的和。
其中 \(|S|\) 表示集合的大小(长度)。
也就问:
op可以是交,并,异或,问\(3^{n-1}\)种情况的长度之和是多少。

思路1

按平时的经验,当求好多种方案的总答案的时候,往往是把某个单独的可以贡献答案的东西取出来,单独看它可以对答案做出多少贡献。

发现元素最多一共3e5种,所以完全可以枚举每一种元素对答案的贡献。

我们考虑,每个数 p 会出现的最后的集合
(因为我们计算的是集合的大小)

我们设集合\(S_i\)之前的集合中,有 \(p_1\) 个集合有p,有 \(p_0\) 个集合没有p。

如果 \(S_i\) 中有数字p

然后我们分别执行三个操作,我们得到:

\[p_1 = 2*(p_1+p_0),p_0 = (p_1+p_0) \]

有p 的集合,经过 \(\cap\cup\) 后还会有p
没有p的集合经过 \(\cup\oplus\) 也会有\(p\)。 (注意 \(S_i\) 中是有\(p\) 的)

如果 \(S_i\) 中没有p,我们可以得到

\[p_1 = 2*p_1,p_0 = p_1 + 3p_0 \]

然后一个想法就出来了,我们用矩阵来进行运算。

思路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

posted @ 2022-11-01 20:54  kingwzun  阅读(35)  评论(0编辑  收藏  举报