正题
题目链接:https://atcoder.jp/contests/arc115/tasks/arc115_d
题目大意
nnn个数字的序列xxx,第xi∈[1,Ai]∩Zx_i\in [1,A_i]\cap Zxi∈[1,Ai]∩Z。要求相邻的不同,求方案数。
1≤n≤5×105,1≤Ai≤1091\leq n\leq 5\times 10^5,1\leq A_i\leq 10^91≤n≤5×105,1≤Ai≤109
解题思路
考虑容斥,如果有kkk个相邻的相等那么容斥系数就是(−1)k(-1)^k(−1)k。那我们把nnn分为若干个连续的相同段,然后每一段的容斥系数分开算就好了,这样就是一个可以dpdpdp的式子了。
设fif_ifi表示以iii结尾的段时的值,那么有转移方程
fi=∑j=0i−1fj×min{Ak}(k∈(j,i])×(−1)i−j−1f_i=\sum_{j=0}^{i-1}f_j\times min\{A_k\}(k\in(j,i])\times (-1)^{i-j-1}fi=j=0∑i−1fj×min{Ak}(k∈(j,i])×(−1)i−j−1
这个min{Ak}min\{A_k\}min{Ak}每次加入一个新的时候会影响一个后缀,用单调栈找到这个后缀,然后把fif_ifi丢进线段树里。
而那个容斥系数就是每次整个线段树乘上一个(−1)(-1)(−1),这个丢到外面处理就好了。
时间复杂度O(nlogn)O(n\log n)O(nlogn)
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;
}