[DS记录]P7482 不条理狂诗曲

command_block

2021-07-18 20:03:20

Personal

为什么我的大脑在读标题的时候自动多塞进去一个“序”字啊…… **题意** : 给出一个长度为 $n$ 的非负整数序列 $A$ ,记 $f(l,r)$ 为 $A[l,r]$ 中选出若干不相邻元素的和的最大值。 求下列式子的值。 $$\sum\limits_{1\leq l\leq r\leq n}f(l,r)$$ 答案可能较大,对 $10^9+7$ 取模。 $n\leq 10^5$ ,时限$\texttt{1s}$。 ------------ 考虑分治,每次计算跨越分界线 $(m,m+1)$ 的所有区间的贡献。 记 $s_{0,l}$ 表示 $A[l,m-1]$ 中出若干不相邻元素的最大和。 记 $s_{1,l}$ 表示 $A[l,m]$ 中出若干不相邻元素的最大和。 记 $s_{0,r}$ 表示 $A[m+2,r]$ 中出若干不相邻元素的最大和。 记 $s_{1,r}$ 表示 $A[m+1,r]$ 中出若干不相邻元素的最大和。 都容易用 DP 线性求出。 此时可以求单个 $f(l,r)$ : $$f(l,r)=\max\big(s_{0,l}+s_{1,r},s_{1,l}+s_{0,r}\big)$$ 写出跨越分界线 $(m,m+1)$ 的所有区间的贡献 : $$\sum_{1\leq l\leq m}\sum_{m+1\leq r\leq n}\max\big(s_{0,l}+s_{1,r},s_{1,l}+s_{0,r}\big)$$ 对每个 $l$ 分别计算。对 $\max$ 的决策进行分类讨论。 $s_{0,l}+s_{1,r}>s_{1,l}+s_{0,r}\quad \Leftrightarrow \quad s_{0,l}-s_{1,l}>s_{0,r}-s_{1,r}$ 于是,将 $r$ 按照 $s_{0,r}-s_{1,r}$ 排序,满足 $s_{0,l}+s_{1,r}>s_{1,l}+s_{0,r}$ 的就是一个前缀,可以二分得出。 贡献用前缀和计算。 复杂度 $O(n\log^2n)$。 ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 100500 using namespace std; const int mod=1000000007; int a[MaxN],ans,sl1[MaxN],sr0[MaxN],p[MaxN]; ll s0[MaxN],s1[MaxN]; bool cmp(int A,int B) {return s0[A]-s1[A]<s0[B]-s1[B];} void solve(int l,int r) { if (l==r){ans=(ans+a[l])%mod;return ;} int m=(l+r)>>1; solve(l,m);solve(m+1,r); s0[m]=0;s0[m-1]=a[m-1]; s1[m]=a[m];s1[m-1]=max(a[m-1],a[m]); for (int i=m-2;i>=l;i--){ s0[i]=max(s0[i+1],s0[i+2]+a[i]); s1[i]=max(s1[i+1],s1[i+2]+a[i]); } s0[m+1]=0;s0[m+2]=a[m+2]; s1[m+1]=a[m+1];s1[m+2]=max(a[m+2],a[m+1]); for (int i=m+3;i<=r;i++){ s0[i]=max(s0[i-1],s0[i-2]+a[i]); s1[i]=max(s1[i-1],s1[i-2]+a[i]); } int tn=0; for (int i=m+1;i<=r;i++)p[++tn]=i; sort(p+1,p+tn+1,cmp); for (int i=1;i<=tn;i++)sl1[i]=(sl1[i-1]+s1[p[i]])%mod; sr0[tn+1]=0;for (int i=tn;i;i--)sr0[i]=(sr0[i+1]+s0[p[i]])%mod; for (int i=l;i<=m;i++){ int tl=0,tr=tn,mid; ll lim=s0[i]-s1[i]; while(tl<tr){ int mid=(tl+tr+1)>>1; if (s0[p[mid]]-s1[p[mid]]<lim)tl=mid; else tr=mid-1; }ans=(ans+s0[i]%mod*tl+s1[i]%mod*(tn-tl)+sl1[tl]+sr0[tl+1])%mod; } } int n; int main() { scanf("%d",&n); for (int i=1;i<=n;i++)scanf("%d",&a[i]); solve(1,n); printf("%d",ans); return 0; } ```