首页 > 其他分享 >CF1591F - Non-equal Neighbours

CF1591F - Non-equal Neighbours

时间:2023-05-07 19:34:31浏览次数:47  
标签:Non 200005 int CF1591F sum equal sg dp 关键点

My solution

首先,我们考虑最暴力的 \(dp\),设 \(dp_{i,j}\) 表示当前处理到第 \(i\) 位,目前序列尾部是 \(j\) 的方案数。这个 \(dp\) 的转移是很容易的。\(dp_{i,j}=\sum_{k=1}^{a_{i-1}}[k\neq j]dp_{i-1,k}\)。但是复杂度也是很寄的,是 \(O(na)\)。

然后我们考虑优化这个暴力,我们发现,因为我们每次都是用上一个的所有减去自身,所以实际上会影响某个位置的答案的只有 \(a_{i-1}\)。如果我们将所有的 \(a_i\) 离散化下来,就能得到若干个左开右闭的区间。而这个区间内的所有的 \(j\) 的转移是相同的。所以如果我们记离散化之后的第 \(i\) 个数是 \(b_i\),\(dp_{i,j}\) 表示 \(dp\) 到第 \(i\) 位,结尾是区间 \(j\) 里的数,对于这个特定的数 的方案数。也就是,真正的“区间里所有数的方案数的总和”其实应该是 \(dp_{i,j}(b_j-b_{j-1})\)。

然后我们就得到转移 \(dp_{i,j}=\sum_{k=1}^{s_{i-1}}(b_k-b_{k-1}-[k=j])dp_{i-1,k}\)。

这样我们就得到一个 \(O(n^2)\) 的做法。但是依然过不去。

考虑优化这个算法。我们发现,\(dp_{i,j}\) 实际上就是对于上一位的所有的和减去上一次在这个位置的方案,也就是 \(totsum-dp_{i-1,j}\),我们就可以用线段树维护 \(dp_{i,j}(b_j-b_{j-1})\),每次转移,\(dp_{i,j}\) 先取反然后加上 \(sum\),因为 \((b_j-b_{j-1})\) 总是不变的,所以 \(dp_{i,j}(b_j-b_{j-1})\) 也取反。对于 \(a_i\) 以上的部分,直接赋成 \(0\)。

我们可以使用乘法加法线段树,维护区间加、全局和、区间乘(\(0\) 或 \(-1\))。我们也可以单独维护清空标记和取反标记。这里用的是第二种。

const int P=998244353;
int n,a[200005],b[200005],m;
struct node{
	int l,r,len,sum,fl,tg,cl;
}sg[800005];
inline void init(int i,int l,int r){
	sg[i].l=l,sg[i].r=r;
	if(l==r){
		sg[i].len=b[l]-b[l-1];
		sg[i].sum=0;
		return;
	}
	init(i<<1,l,(l+r)>>1);
	init(i<<1|1,((l+r)>>1)+1,r);
	sg[i].len=(sg[i<<1].len+sg[i<<1|1].len)%P;
}
inline void pushdown(int i){
	if(sg[i].cl)sg[i].sum=0;
	if(sg[i].fl)sg[i].sum=(P-sg[i].sum);
	if(sg[i].tg)sg[i].sum=(sg[i].sum+1ll*sg[i].tg*sg[i].len%P)%P;
	if(sg[i].l!=sg[i].r){
		if(sg[i].cl)sg[i<<1].cl=1,sg[i<<1].tg=0,sg[i<<1|1].cl=1,sg[i<<1|1].tg=0;
		if(sg[i].fl){
			sg[i<<1].tg=(P-sg[i<<1].tg);
			sg[i<<1|1].tg=(P-sg[i<<1|1].tg);
			sg[i<<1].fl^=1,sg[i<<1|1].fl^=1;
		}
		sg[i<<1].tg=(sg[i<<1].tg+sg[i].tg)%P;
		sg[i<<1|1].tg=(sg[i<<1|1].tg+sg[i].tg)%P;
	}
	sg[i].fl=0,sg[i].tg=0,sg[i].cl=0;
}
inline void flip(int i,int l,int r){
	pushdown(i);
	if(sg[i].l>r||sg[i].r<l)return;
	if(sg[i].l>=l&&sg[i].r<=r){
		sg[i].fl^=1;
		pushdown(i);
		return;
	}
	flip(i<<1,l,r);
	flip(i<<1|1,l,r);
	sg[i].sum=(sg[i<<1].sum+sg[i<<1|1].sum)%P;
}
inline void add(int i,int l,int r,int x){
	pushdown(i);
	if(sg[i].l>r||sg[i].r<l)return;
	if(sg[i].l>=l&&sg[i].r<=r){
		sg[i].tg=(sg[i].tg+x)%P;
		pushdown(i);
		return;
	}
	add(i<<1,l,r,x);
	add(i<<1|1,l,r,x);
	sg[i].sum=(sg[i<<1].sum+sg[i<<1|1].sum)%P;
}
inline void clear(int i,int l,int r){
	pushdown(i);
	if(sg[i].l>r||sg[i].r<l)return;
	if(sg[i].l>=l&&sg[i].r<=r){
		sg[i].cl=1;
		pushdown(i);
		return;
	}
	clear(i<<1,l,r);
	clear(i<<1|1,l,r);
	sg[i].sum=(sg[i<<1].sum+sg[i<<1|1].sum)%P;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	rp(i,n)cin>>a[i];
	rp(i,n)b[++m]=a[i];
	sort(b+1,b+1+m);
	m=unique(b+1,b+1+m)-b-1;
	rp(i,n)a[i]=lower_bound(b+1,b+1+m,a[i])-b;
	init(1,1,m);
	add(1,1,a[1],1);
	rep(i,2,n){
		int sum=sg[1].sum;
		clear(1,a[i]+1,m);
		flip(1,1,m);
		add(1,1,a[i],sum);
	}cout<<sg[1].sum%P<<endl;
	return 0;
}
//Crayan_r

容斥

考虑容斥。

设关键点是 \(b_i=b_{i+1}\) 的情况,我们显然不需要这样的情况。所以我们需要的是恰好出现 \(0\) 个关键点的序列个数。

考虑容斥,设 \(r_k\) 表示有 \(k\) 个关键点的方案数,那么答案就是 \(\sum_{i=0}^{n-1}(-1)^kr_k\)。考虑如何快速计算。

我们发现,关键点不好计算,因为不同的关键点不是独立的。但是我们可以把相邻的关键点看作是合并,也就是把关键点转化成了对整个序列的划分。划分方案中,同一组内的一定相同,不同的可能相同。

考虑 \(dp\),设 \(dp_{i,j}\) 表示当前 \(dp\) 到位置 \(i\),划分出了奇数/偶数段的方案数。然后考虑转移,\(dp_{i,j}=\sum_{x=0}^{i-1}dp_{x,j\oplus 1}\min_{p=x+1}^i\{a_p\}\)。考虑优化。

我们发现,如果我们事先用单调栈预处理每个数前面第一个小于它的位置 \(y\),那么对于 \(x<y\) 的情况,两者是相同的,可以直接用 \(dp_{y,j}\) 代替(注意 \(y=0\) 除外,因为我们实际上要的是 \(y\) 左边的部分。然后,\(x\ge y\) 的部分没有比 \(a_i\) 更小的,所以就是 \(a_i\sum f_{k,j\oplus 1}\),直接预处理前缀和即可。

最后统计答案,注意段数的奇偶性不等于关键点的奇偶性,需要重新讨论。

复杂度 \(O(n)\)。

const int P=998244353;
int n,a[200005],s[200005],top,l[200005];
int f[200005][2],sum[200005][2];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	rp(i,n)cin>>a[i];
	a[0]=0,s[++top]=0;
	rp(i,n){
		while(a[i]<=a[s[top]])top--;
		l[i]=s[top];
		s[++top]=i;
	}
	f[0][0]=1,f[0][1]=0;
	sum[0][0]=1;
	rp(i,n)rd(j,2){
		f[i][j]=((l[i]==0?0:f[l[i]][j])+1ll*(sum[i-1][j^1]-(l[i]==0?0:sum[l[i]-1][j^1])+P)%P*a[i]%P)%P;
		sum[i][j]=(sum[i-1][j]+f[i][j])%P;
	}
	cout<<(f[n][n&1]-f[n][(n&1)^1]+P)%P<<endl;
	return 0;
}
//Crayan_r

标签:Non,200005,int,CF1591F,sum,equal,sg,dp,关键点
From: https://www.cnblogs.com/jucason-xu/p/17379691.html

相关文章

  • 在访问UserController控制器下的connonParam方法的时候无法访问,报404
    在访问UserController控制器下的connonParam方法的时候无法访问,报404服务已经起来了  但是访问还是失败了找不到资源 springmvc配置类也配好了路径,最后发现是 ServletContainersInitConfig的getServletMappings()方法没有设置交由springmvc的拦截请求,修改! ......
  • ERROR: Could not find a version that satisfies the requirement cv2 (from version
    现象导入cv2时,报如下的错误ERROR:Couldnotfindaversionthatsatisfiestherequirementcv2(fromversions:none)解决方案win+R打开命令行,输入 pipinstallopencv-python,下载opencv,等待下载完成即可。下载有点慢,耐心等待一下。 ......
  • 关于java中的equal
    正常情况下的equal方法是比较两者之间的id。如果需要它实现其他的问题,可以通过重写这个方法。idea自带了重写equal的快捷方式。右键生成中的equals()和hashCode()就可以帮助解决这个问题。选择需要在equal中比较的项,比如需要得到id和pwd是否相同的结果,就可以只勾选他们两个。......
  • object和equals
    1.Object详解1.1==比较细节3181.1.1例==判断引用类型,判断地址是否相等318代码在com.stulzl.object_.包中Object_equalspackagecom.stulzl.object_;//判断引用类型,判断地址是否相等318publicclassObject_equals{publicstaticvoidmain(String[]args){......
  • springSecurity过滤器之AnonymousAuthenticationFilter
    SpringSecurity提供了匿名登录功能,让我们不登录也能访问。比如/anoy路径及子路径都能匿名访问,配置如下:@ConfigurationpublicclassMySecurityConfigextendsWebSecurityConfigurerAdapter{@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException......
  • isEqual和==区别
    再看文档时留意到isEqual方法,但是我们比较的时候有时候就用==来比较,这2个有似乎没区别呢?网上有人说==来比较指针,isEqual是比较内容,其实这个话如果深究起来,并不是那么准确,我用代码测试了下:1.str1=@"111";2.str2=@"111";3.4.if([str1isEqual:str2]){5.NSLog......
  • c语言报错 [Error] invalid initialization of non-const reference of type 'LinkQue
     进行地址传递是出现报错临时值不能作为非常量引用参数进行传递所以需要在main函数中·重新定义指针传递 ......
  • Python很多时候要从键盘连续输入一个数组,并用空格隔开;Python爬取一些数据;python pip安
    Python要从键盘连续输入一个数组,并用空格隔开,Python中的实现方法如下:str=input(‘以空格为间隔连续输入一个数组:’)然后在键盘中输入,会·得到的str为一个字符串,要将其转为一个列表有两种方法方法一:a=[int(n)forninstr_in.split()]方法二:a=list(map(int,str.strip().sp......
  • python中global 和 nonlocal 的作用域
    python引用变量的顺序: 当前作用域局部变量->外层作用域变量->当前模块中的全局变量->python内置变量。一globalglobal关键字用来在函数或其他局部作用域中使用全局变量。但是如果不修改全局变量也可以不使用global关键字。1gcount=023defglobal_test():4gcount+......
  • equal symbol expected
    这样嵌套会异常<html:textproperty="dateCreated"value=<bean:writename="dateCreated"property="dateCreated"/>/>像下边这样写就不会<html:textproperty="dateCreated"value="${dateCreated......