P7482

思路

分治 的贡献。

对于一个区间计算答案可以用 dp 完成。以 为交界合并左右的 dp 值。设 表示区间 或区间 ,是否选 的答案。

记跨过 的贡献为

后面两个直接做,前面的对于每个 拆开 max 计算。

排序,二分 的位置,记录 的后缀和即可。

其余的递归 解决。

然后发现一个 sub 都没过。原因是不一定一定要选 ,将 的定义改为区间不考虑任何限制的最大值,强行当作选 位置即可。

code

int n,a[maxn],ans;
int f[maxn][2],g[maxn];
int b[maxn],len,sum[maxn];
void sovle(int l,int r){
	if(l==r){
		(ans+=a[l])%=mod;
		return ;
	}
	int mid=l+r>>1;
	f[mid][0]=0,f[mid][1]=a[mid];
	if(mid-1>=l)f[mid-1][0]=a[mid-1],f[mid-1][1]=max(a[mid-1],a[mid]);
	for(int i=mid-2;i>=l;i--){
		f[i][0]=max(f[i+1][0],f[i+2][0]+a[i]);
		f[i][1]=max(f[i+1][1],f[i+2][1]+a[i]);
	}
	f[mid+1][0]=0,f[mid+1][1]=a[mid+1];
	if(mid+2<=r)f[mid+2][0]=a[mid+2],f[mid+2][1]=max(a[mid+2],a[mid+1]);
	for(int i=mid+3;i<=r;i++){
		f[i][0]=max(f[i-1][0],f[i-2][0]+a[i]);
		f[i][1]=max(f[i-1][1],f[i-2][1]+a[i]);
	}
	int sl=0,sr=0;
	for(int i=l;i<=mid;i++)(sl+=f[i][0])%=mod;
	for(int i=mid+1;i<=r;i++)(sr+=f[i][0])%=mod;
	(ans+=sl*(r-mid)+sr*(mid-l+1))%=mod;
//	cout<<l<<" "<<r<<" "<<ans<<"\n"; 
	for(int i=l;i<=r;i++)g[i]=f[i][1]-f[i][0];
	len=0; 
	for(int i=mid+1;i<=r;i++)b[++len]=g[i];
	sort(b+1,b+len+1);sum[len+1]=0;
	for(int i=len;i>=1;i--)sum[i]=(sum[i+1]+b[i]%mod+mod)%mod;
	for(int i=l;i<=mid;i++){
		int p=upper_bound(b+1,b+len+1,g[i])-b;
		(ans+=(g[i]%mod+mod)*(p-1)+sum[p])%=mod;
//		cout<<i<<" "<<g[i]<<" "<<p<<" "<<sum[p]<<"\n";
	}
	sovle(l,mid),sovle(mid+1,r);
}