F‘
题意:
就是给你n个区间,每个区间包含一段连续的值,然后这就是一个集合。现在有个式子|(((S1 op1 S2) op2 S3) op3 S4) … opn−1 Sn| 。然后其中的op,分别可以是∪:两个集合元素的并集。∩:两个集合的元素交集。⊕:两个集合中元素只在某一个集合中出现的所有元素。现在每个opi都有3中选择,现在问你3n-1种情况中,把答案的总和求起来。对于那个公式,就是问你经过顺序操作后,整个集合中还有多少元素。
思考:
- 当时看完题就感觉这题好像不是那么简单,这那么多方案怎么求?不过按平时的经验,当求好多种方案的总答案的时候,往往是把某个单独的可以贡献答案的东西取出来,单独看它可以对答案做出多少贡献。
- 那么想到这里就很明了了,先看答案求的是元素个数的总答案,那么对于小单元就是每一个元素,又发现元素最多一共3e5种,所以完全可以枚举每一种元素对答案的贡献。
- 比如枚举到i,怎么看贡献呢?既然要看贡献,肯定是要把枚举的元素和集合关系到一起,比如我可以看看这个元素出现的集合中,最大的集合下标是多少。也就是最晚是第几个线段包含这个元素,那么我可以对值维护线段树,维护出每个值出现的最晚的线段的下标。
- 维护出来之后发现,比如i出现的最晚线段下标是pos,一共有n个线段,那么方案数怎么看?可以发现对于pos之后的所有集合都是不包含i的,所以对于后面的操作都只能用U或者⊕,所以后面一共n-pos+1个op,方案是2n-pos。但是pos前面的怎么办?前面还有线段可能包含i呢?看起来挺复杂,实际上就两种情况,到pos-1的时候包含元素i,到pos-1的时候不包含元素i,对于这两种情况转移到pos,都是✖2,所以包含元素i和不包含元素i就可以合并在一起了。推的式子:
- 不过有个值得注意的就是,当元素i永远没出现的时候,求continue了。并且当是第1个元素的时候,前面并没有元素了,所以方案数就是2n-1。
- 反正一定要多多思考,把这些看起来复杂的问题,转化为简单的模式,联想联想以前求答案的各种操作。
代码:
#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;
}
总结:
多多思考。