ARC115ELEQ and NEQ【容斥,dp,线段树】

正题

题目链接:https://atcoder.jp/contests/arc115/tasks/arc115_d


题目大意

\(n\)个数字的序列\(x\),第\(x_i\in [1,A_i]\cap Z\)。要求相邻的不同,求方案数。

\(1\leq n\leq 5\times 10^5,1\leq A_i\leq 10^9\)


解题思路

考虑容斥,如果有\(k\)个相邻的相等那么容斥系数就是\((-1)^k\)。那我们把\(n\)分为若干个连续的相同段,然后每一段的容斥系数分开算就好了,这样就是一个可以\(dp\)的式子了。

\(f_i\)表示以\(i\)结尾的段时的值,那么有转移方程

\[f_i=\sum_{j=0}^{i-1}f_j\times min\{A_k\}(k\in(j,i])\times (-1)^{i-j-1} \]

这个\(min\{A_k\}\)每次加入一个新的时候会影响一个后缀,用单调栈找到这个后缀,然后把\(f_i\)丢进线段树里。

而那个容斥系数就是每次整个线段树乘上一个\((-1)\),这个丢到外面处理就好了。

时间复杂度\(O(n\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=5e5+10,M=N<<2,P=998244353;
ll n,a[N],q[N],f[N];
ll w[M],lazy[M],v[M];
void Downdata(ll x){
	if(!lazy[x])return;
	w[x*2]=v[x*2]*lazy[x]%P;
	w[x*2+1]=v[x*2+1]*lazy[x]%P;
	lazy[x*2]=lazy[x*2+1]=lazy[x];
	return;
}
void Change(ll x,ll L,ll R,ll l,ll r,ll c){
	if(L==l&&R==r){lazy[x]=c;w[x]=v[x]*c%P;return;}
	ll mid=(L+R)>>1;Downdata(x);
	if(r<=mid)Change(x*2,L,mid,l,r,c);
	else if(l>mid)Change(x*2+1,mid+1,R,l,r,c);
	else Change(x*2,L,mid,l,mid,c),Change(x*2+1,mid+1,R,mid+1,r,c);
	w[x]=(w[x*2]+w[x*2+1])%P;v[x]=(v[x*2]+v[x*2+1])%P;return; 
}
void Insert(ll x,ll L,ll R,ll pos,ll c){
	if(L==R){v[x]=c;w[x]=c*lazy[x]%P;return;}
	ll mid=(L+R)>>1;Downdata(x);
	if(pos<=mid)Insert(x*2,L,mid,pos,c);
	else Insert(x*2+1,mid+1,R,pos,c);
	w[x]=(w[x*2]+w[x*2+1])%P;v[x]=(v[x*2]+v[x*2+1])%P;return;
}
signed main()
{
	scanf("%lld",&n);
	ll top=1;Insert(1,1,n,1,P-1);
	for(ll i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		while(top>0&&a[i]<a[q[top]])top--;
		Change(1,1,n,q[top]+1,i,a[i]);q[++top]=i;
		f[i]=(i&1)?(P-w[1]):w[1];
		if(i!=n)Insert(1,1,n,i+1,P-w[1]);
	}
	printf("%lld\n",f[n]);
	return 0;
}
原文地址:https://www.cnblogs.com/QuantAsk/p/14578706.html