CFdiv2-Intersection and Union-(线段树+转化求贡献)

F

题意:
就是给你n个区间,每个区间包含一段连续的值,然后这就是一个集合。现在有个式子|(((S1 op1 S2) op2 S3) op3 S4) … opn−1 Sn| 。然后其中的op,分别可以是∪:两个集合元素的并集。∩:两个集合的元素交集。⊕:两个集合中元素只在某一个集合中出现的所有元素。现在每个opi都有3中选择,现在问你3n-1种情况中,把答案的总和求起来。对于那个公式,就是问你经过顺序操作后,整个集合中还有多少元素。

思考:

  1. 当时看完题就感觉这题好像不是那么简单,这那么多方案怎么求?不过按平时的经验,当求好多种方案的总答案的时候,往往是把某个单独的可以贡献答案的东西取出来,单独看它可以对答案做出多少贡献。
  2. 那么想到这里就很明了了,先看答案求的是元素个数的总答案,那么对于小单元就是每一个元素,又发现元素最多一共3e5种,所以完全可以枚举每一种元素对答案的贡献。
  3. 比如枚举到i,怎么看贡献呢?既然要看贡献,肯定是要把枚举的元素和集合关系到一起,比如我可以看看这个元素出现的集合中,最大的集合下标是多少。也就是最晚是第几个线段包含这个元素,那么我可以对值维护线段树,维护出每个值出现的最晚的线段的下标。
  4. 维护出来之后发现,比如i出现的最晚线段下标是pos,一共有n个线段,那么方案数怎么看?可以发现对于pos之后的所有集合都是不包含i的,所以对于后面的操作都只能用U或者⊕,所以后面一共n-pos+1个op,方案是2n-pos。但是pos前面的怎么办?前面还有线段可能包含i呢?看起来挺复杂,实际上就两种情况,到pos-1的时候包含元素i,到pos-1的时候不包含元素i,对于这两种情况转移到pos,都是✖2,所以包含元素i和不包含元素i就可以合并在一起了。推的式子:
    在这里插入图片描述
  5. 不过有个值得注意的就是,当元素i永远没出现的时候,求continue了。并且当是第1个元素的时候,前面并没有元素了,所以方案数就是2n-1
  6. 反正一定要多多思考,把这些看起来复杂的问题,转化为简单的模式,联想联想以前求答案的各种操作。

代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);

using namespace std;
const int mod = 998244353,inf = 1e18;
const int N = 3e5+10,M = 2010;

int T,n,m,k;
int va[N];

struct seg_tree{
	#define node_l node<<1
	#define node_r node<<1|1
	struct node{
		int L,R;
		int maxn,laz;
	}t[4*N];
	void pushup(int node)
	{
		t[node].maxn = (t[node_l].maxn,t[node_r].maxn);
	}
	void pushdown(int node)
	{
		int laz = t[node].laz;
		if(laz==0) return ;
		t[node_l].maxn = max(t[node_l].maxn,laz);
		t[node_l].laz = max(t[node_l].laz,laz);
		t[node_r].maxn = max(t[node_r].maxn,laz);
		t[node_r].laz = max(t[node_r].laz,laz);
		t[node].laz = 0;
		return ;
	}
	void build(int node,int l,int r)
	{
		t[node].L = l,t[node].R = r;
		if(l==r)
		{
			return ;
		}
		int mid = (l+r)>>1;
		build(node_l,l,mid);build(node_r,mid+1,r);
		pushup(node);
	}
	void update(int node,int l,int r,int value)
	{
		if(t[node].L>=l&&t[node].R<=r)
		{
			t[node].maxn = max(t[node].maxn,value);
			t[node].laz = max(t[node].laz,value);
			return ;
		}
		pushdown(node);
		int mid = (t[node].L+t[node].R)>>1;
		if(r<=mid) update(node_l,l,r,value);
		else if(l>mid) update(node_r,l,r,value);
		else update(node_l,l,mid,value),update(node_r,mid+1,r,value);
		pushup(node);
	}
	int query(int node,int l,int r)
	{
		if(t[node].L>=l&&t[node].R<=r) return t[node].maxn;
		pushdown(node);
		int mid = (t[node].L+t[node].R)>>1;
		if(r<=mid) return query(node_l,l,r);
		else if(l>mid) return query(node_r,l,r);
		else return max(query(node_l,l,mid),query(node_r,mid+1,r));
		pushup(node);	
	}
}tmax;

int ksm(int a,int b)
{
	int sum = 1;
	while(b)
	{
		if(b&1) sum = sum*a%mod;
		a = a*a%mod;
		b >>= 1;
	}
	return sum;
}

signed main()
{
	IOS;
	cin>>n;
	tmax.build(1,0,3e5);
	for(int i=1;i<=n;i++)
	{
		int a,b;cin>>a>>b;
		tmax.update(1,a,b,i);
	}
	int ans = 0;
	for(int i=0;i<=3e5;i++)
	{
		int pos = tmax.query(1,i,i);
		if(pos==0) continue;
		int l = max(0ll,pos-2),r = min(n-1,n-pos+1);
		ans = (ans+ksm(3,l)%mod*ksm(2,r)%mod)%mod;
	}
	cout<<ans;
	return 0;
}

总结:
多多思考。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值