Codeforces Round #531 (Div. 3) E. Monotonic Renumeration(离散化+区间合并)

题意

给你一个序列,要你根据这个序列构造一个新序列,

要求新序列的下一个数只能与上一个数相同或为上一个数+1

且原序列中相同的数,在新序列对应的位置的数也得相同

在新序列第一个数恒为0的情况下,

求有多少种构造序列的方式

思路来源

cf某神仙代码

题解

基础部分:

显然把一个数最开始出现和最后出现的位置理解成一个区间则区间中的数均相同

若区间i与区间j相交,显然i和j的数也均需相同,合并为统一区间

那么就是求除了第一个数所在的区间以外,还有num个独立的区间,

每个区间对应与上一个区间相同或上一个区间+1两种选择,

答案即为modpow(2,num,mod),modpow为快速幂

技巧:

①离散化

将原序列排序,二分找到原序列在对应排序序列中第一次出现的位置,即为原序列的最小rank

把原序列的值赋为rank值,这样1e9就缩到2e5了

简直不能再巧妙好嘛!以前天天说离散化都不会这样离散化真是菜哭了55555

②区间合并

开一个mx[]数组记录a[i]在数组中出现的最后一次下标,增序遍历覆盖一下就ojbk

增序遍历位置i,nowr更新前面已存在的区间的可达最右端点

如果i在nowr左边,说明被以前的区间覆盖了,不用开新区间

否则和之前的区间没有交集,开一个新区间,num++

每次更新nowr=max(nowr,mx[a[i]])

最后求一下2的num次方%mod就好了,快速幂也能写懒得写了

代码

#include
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int mod=998244353;
int n,a[maxn],b[maxn],num,mx[maxn];
int main()
{
	scanf("%d",&n);
	for(int i=0;inowr)num++;//没到 新开一个区间 注意b1==0恒成立 被b1吞并的都得为0 
	 nowr=max(nowr,mx[a[i]]);//开的多个区间 都能吞并nowr以左的数 
	}
	for(int i=0;i=mod)ans%=mod;
	}
	printf("%I64d\n",ans);
	return 0;
}

 

你可能感兴趣的:(组合数学(容斥原理))