半路进军的ACMer一枚 | 医学杂交|

kingwzun

园龄:3年6个月粉丝:111关注:0

2022-11-01 20:54阅读: 36评论: 0推荐: 0

L - Intersection and Union Gym - 103993L (线段树)

题意

给定 n(n3105) 个区间[liri](0li,ri3105)
每个区间可以看成包含区间内的数的集合 Si
定义集合的操作有 ,, 。其中 SaSb=SaSb(SaSb)
给定一系列的操作符号 op1,op2,op3......(opi())
你需要计算对于 op 所有的可能下
image

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

思路1

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

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

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

我们设集合Si之前的集合中,有 p1 个集合有p,有 p0 个集合没有p。

如果 Si 中有数字p

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

p1=2(p1+p0),p0=(p1+p0)

有p 的集合,经过 后还会有p
没有p的集合经过 也会有p。 (注意 Si 中是有p 的)

如果 Si 中没有p,我们可以得到

p1=2p1,p0=p1+3p0

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

思路2

虽然直接矩阵是可以计算的,但是,我们漏了一点,就是进行了i次运算可以得到 3i 个集合。

那么 p1+p0=3i

例如,有个p最后一次出现在S_4中。
则,前面一共有32 个集合,和S4 运算后,有232 个集合都含有p。
p1=2(p1+p0)=232
因为后面就没有拥有p的集合了。那么后面,每经过一个集合,含有p的集合数目就会乘以2,也
就是
2i32

总结一下就是,如果一个数字最后一次出现在集合Sw 中:

  • 前面有 3w2 个集合
  • 后续需要经过nw+1 次乘以2

2nw+13w2 就是最终,含有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

本文作者:kingwzun

本文链接:https://www.cnblogs.com/kingwz/p/16849142.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   kingwzun  阅读(36)  评论(0编辑  收藏  举报
历史上的今天:
2021-11-01 解决Visua SC的运行时不能输出数据
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示