• SP22343 Norma--序列分治


    Norma

    传送门

    题意简化:

    定义一个区间的贡献为 (max*min*len),求给定序列中所有子区间的总贡献和

    题解

    考虑 (O(n*log_2n)) 的复杂度的做法
    数据结构??? yzhx太菜了,不会怎么O(n)枚举所有区间
    还是考虑分治吧

    每次对于每个区间的贡献则等于:
    左半边区间的贡献+右半边区间的贡献+跨越mid的区间贡献

    所以现在分治的大体思路就出来了,先递归处理左右半边,在加上跨越左右区间的影响

    我们再来观察这个式子: (max*min*len)
    想想有什么可优化的部分呢?
    显然,对于每个区间的max和min在适当扩张区间长度的情况下是不会改变的
    所以我们暴力求的话则会重复求很多次

    有了大致思路,那么,我们现在来考虑怎么处理跨mid的子区间贡献
    设当前区间的左端点为 L ,右端点为 R , mid 为该区间的中间位置
    对于跨越mid的子区间
    枚举子区间左端点 i ,先假设它的右端点就是mid,那么我们再慢慢向右拓展右端点,并加入答案
    设: 子区间i~j的最大值为max,最小值为min,
    在拓展过程中 min 第一次改变的位置是 p , max 第一次改变的位置是 q , p<q (反过来也差不多)
    所以所有以 i 为左端点,右端点j在 mid+1 ~ R 之间的子区间都可以被分为三种类型:

    j < p 时 ( (min*max) 不变) :

    [ans+= min*max* sum_{j=mid+1}^{p-1}(j-i+1) ]

    p<=j<q 时(只有 min 和区间长度发生改变):

    [ans+= max * sum_{j=p}^{q-1} min[j] * (j-i+1) ]

    --> $$ ans+=max* sum_{j=p}^{q-1} min[j] j+max * (1-i)sum_{j=p}^{q-1} min[j]) $$

    q<j<=r 时(全都改变):

    [ans+= sum_{j=p}^{q-1} max[j]*min[j] * (j-i+1) ]

    --> $$ ans+= sum_{j=p}^{q-1} max[j]min[j] j+(1-i)sum_{j=p}^{q-1} max[j]min[j]) $$
    然后我们再把 $$max[i],min[i],max[i]i,min[i]i,max[i]min[i],max[i]min[i]*i $$都用前缀和记录就好

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define re register
    #define in inline
    #define get getchar()
    #define ll long long
    in int read()
    {
    	int t=0; char ch=get;
    	while(ch<'0' || ch>'9') ch=get;
    	while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
    	return t;
    }
    const int mod=1e9;
    const int _=500002;
    int n;
    ll mul_mx[_],mul_mn[_],ans,sum_mx[_],sum_mn[_],mx[_],mn[_],a[_],mnmx[_],mul_mnmx[_];
    in ll add(ll a,ll b) { //加法取模
    	return (((a%mod)+(b%mod))+mod)%mod;
    }
    in ll mul(ll a,ll b) { //乘法取模
    	return ((a%mod*b%mod)+mod)%mod;
    }
    in ll getsum(ll a,ll b) { //高斯求和
    	return ((a+b)*(b-a+1)/2)%mod;
    }
    in void work(int l,int r)
    {
    //	cout<<l<<' '<<r<<" :: "<<endl;
    	if(l==r)  { ans=add(ans,mul(a[l],a[l]));return;}
    	int mid=l+r>>1;
    	work(l,mid);
    	work(mid+1,r);
    	mx[mid]=mn[mid]=a[mid];
    	sum_mx[mid]=sum_mn[mid]=mul_mx[mid]=mul_mn[mid]=mul_mnmx[mid]=mnmx[mid]=0;
    	for(re int i=mid+1;i<=r;i++) {
    		mx[i]=max(mx[i-1],a[i]);
    		mn[i]=min(mn[i-1],a[i]);
    		sum_mx[i]=add(sum_mx[i-1],mx[i]);
    		sum_mn[i]=add(sum_mn[i-1],mn[i]);
    		mul_mx[i]=add(mul_mx[i-1],mul(mx[i],i));
    		mul_mn[i]=add(mul_mn[i-1],mul(mn[i],i));
    		mul_mnmx[i]=add(mul_mnmx[i-1],mul(mul(mx[i],mn[i]),i));
    		mnmx[i]=add(mnmx[i-1],mul(mx[i],mn[i]));
    	} //预处理
    	ll maxx=0,minn=0x3f3f3f3f3f3f3f3f;
    	for(re int p=mid+1,q=mid+1,i=mid;i>=l;i--)
    	{
    		minn=min(minn,a[i]),maxx=max(maxx,a[i]);
    		while(p<=r&&minn<a[p]) p++;
    		while(q<=r&&maxx>a[q]) q++; //找到p和q
    		if(p<q)
    		{
    			ans=add(ans,mul(mul(minn,maxx),getsum(mid-i+2,p-i)));
    			ans=add(ans,add(mul(maxx,add(mul_mn[q-1],-mul_mn[p-1])),-mul(mul(i-1,maxx),add(sum_mn[q-1],-sum_mn[p-1]))));
    			ans=add(ans,add(add(mul_mnmx[r],-mul_mnmx[q-1]),mul(add(1,-i),add(mnmx[r],-mnmx[q-1]))));
    		} //套上之前讲的三个式子
    		else
    		{
    			ans=add(ans,mul(mul(minn,maxx),getsum(mid-i+2,q-i)));
    			ans=add(ans,add(mul(minn,add(mul_mx[p-1],-mul_mx[q-1])),-mul(mul(i-1,minn),add(sum_mx[p-1],-sum_mx[q-1]))));
    			ans=add(ans,add(add(mul_mnmx[r],-mul_mnmx[p-1]),mul(add(1,-i),add(mnmx[r],-mnmx[p-1]))));
    		}
    	}
    }
    int main()
    {
    	n=read();
    	for(re int i=1;i<=n;i++) a[i]=read();
    	work(1,n);
    	cout<<ans<<endl;
    }
    
  • 相关阅读:
    Spring面试,IoC和AOP的理解
    WEB打印(jsp版)
    Spring事务管理机制的实现原理-动态代理
    spring面试题
    oracle PLSQL基础学习
    oracle创建表空间
    WM_CONCAT字符超过4000的处理办法
    Oracle 数据泵使用详解
    Oracle 数据泵详解
    linux下启动oracle
  • 原文地址:https://www.cnblogs.com/yzhx/p/11734928.html
Copyright © 2020-2023  润新知