SPOJ-NORMA2 & bzoj3745php
题意概要:给定一个正整数序列 \(\{a_i\}\),求c++
\[\sum_{i=1}^n\sum_{j=i}^n(j-i+1)\min(a_i,a_{i+1},\cdots,a_j)\max(a_i,a_{i+1},\cdots a_j)\]git
\(n\leq 5\times 10^5\)算法
这题正解是一个完美的 \(O(n\log n)\) 分治,但比较麻烦,鉴于这个分治作法已经漫天飞了,因此这里不讲那个算法函数
我在考场上在最后二十分钟想到了并打出了另外一个分治作法,很是很好写跑得也很快,最终能够 AC优化
能够考虑对于一个序列 \(\{a_i\}\),找到其最大值 \(mx\) 与最小值 \(mi\),有大量区间都是以这两点为最值点的,而同时这些区间的左右端点分别都是连续的,因此能够考虑将这些区间一块儿计算spa
具体的,若找到的最大值与最小值分别在 \(p_1,p_2\) 取到(不妨设 \(p_1\leq p_2\)),则以这二者为最值点的区间 \([l,r]\) 知足 \(1\leq l\leq p_1,p_2\leq r\leq n\),这些区间的长度和能够 \(O(1)\) 算出,也便可以 \(O(1)\) 算出这些区间的贡献code
进一步的,须要加上其余不是 同时以这二者为最值点 的区间贡献。设统计左右端点都在 \([l,r]\) 内的区间贡献也即刚刚这一步处理为函数 \(f(l,r)\),则其余区间的贡献即 \(f(l,p_2-1)+f(p_1+1,r)-f(p_1+1,p_2-1)\)(因为前面两个式子中重复计算了左右端点都在 \([p_1+1,p_2-1]\) 内的区间贡献,因此须要第三个函数去减去这部分多余的贡献)get
因此如今能够获得一个基本的作法(统计 \([l,r]\) 区间):it
这个作法慢成龟龟,而后我灵机一动:分治下去的区间不是会继续使用当前最值点为最值点吗?(即 \([l,p_2-1]\) 会使用 \(p_1\) 为最值点,进而可能再次调用区间 \([p_1+1,p_2-1]\),这里的统计就冗余了,若是加个记忆化那么原来每次分出三个区间就能够均摊成两个了……)
而后就加了一下 \(map\) 的记忆化,极限数据只须要 \(0.4s\)
以前证了一波伪的复杂度 \(O(n\log n)\),后来被同校 dalao 精心卡掉了 虽然构造了一个多小时
实际上复杂度是 \(O(n^2\log n)\) 的,那个 \(\log\) 仍是 \(map\) 的复杂度 ~~没错这是个暴力,但很难卡满,在考试中、spoj和bzoj上都没能卡掉我♪(^∇^*)~~
实际运行效率很高,未经st表优化的代码在bzoj上跑到 \(\mathrm{rank6}\),比我写的正解快一倍,同时代码也很短很好写 毕竟是在十分钟内写完调完的,只有 \(\mathrm{1.2k}\)
因为想到这个解法时时间紧迫,没来得及写 \(st\) 表作 \(\mathrm{rmq}\) 但仍是过掉了
#include <bits/stdc++.h> using namespace std; typedef long long ll; template <typename _tp> inline _tp read(_tp&x){ char c11=getchar(),ob=0;x=0; while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1; while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x; } const int N=501000,p=1e9,inf=0x3f3f3f3f; int a[N],n; map <int,int> mp[N]; inline int getsum(int l,int r){return 1ll*(l+r)*(r-l+1)/2%p;} inline int qm(int x){while(x<0)x+=p;while(x>=p)x-=p;return x;} int force(int l,int r){ int res(0); for(int i=l;i<=r;++i){ int mx=-inf,mi=inf; for(int j=i;j<=r;++j){ mx=max(mx,a[j]); mi=min(mi,a[j]); res=qm(res+1ll*(j-i+1)*mi%p*mx%p); } }return res; } int solve(int l,int r){ if(l>r)return 0; if(mp[l].find(r)!=mp[l].end()) return mp[l][r]; if(r-l<=10) return mp[l][r]=force(l,r); int mx=-inf,mxd; int mi=inf,mid; for(int i=l;i<=r;++i){ if(a[i]>mx)mx=a[i],mxd=i; if(a[i]<mi)mi=a[i],mid=i; } int L=min(mxd,mid),dl=L-l+1; int R=max(mxd,mid),dr=r-R+1; int dx=R-L-1,res(0); if(dl>dr)swap(dl,dr); for(int i=1;i<=dl;++i) res=qm(res+getsum(i+dx+1,i+dx+dr)); res=1ll*res*mx%p*mi%p; return mp[l][r]=qm(res+qm(solve(l,R-1)+solve(L+1,r))-solve(L+1,R-1)); } int main(){ read(n); for(int i=1;i<=n;++i)read(a[i]); printf("%d\n",solve(1,n)); return 0; }