JZOJ 5695. 【GDSOI2018模拟4.26】链上二次求和

题目

有一条长度为n的链( 1i<n ∀ 1 ≤ i < n ,点i与点i+1之间有一条边的无向图),每个点有一个整数权值,第i个点的权值是ai。现在有 m 个操作,每个操作如下:
操作 1(修改):给定链上两个节点 u、v 和一个整数 d,表示将链上u到 v唯一的简单路径上每个点的权值都加上d。
操作 2(询问):给定两个正整数 l、r,表示求链上所有节点个数大于等于 l且小于等于r的简单路径的节点权值和之和。由于答案很大,只用输出对质数1,000,000,007 取模的结果即可。
一条节点个数为 k 的简单路径的节点权值和为这条路径上所有 k 个节点(包括端点)的权值之和,而本题中要求的是对所有满足要求的简单路径,求这一权值和的和。
由于是无向图,路径也是无向的,即点1到点2的路径与点2到点1的路径是同一条,不要重复计算。

题解

别被 u>v u > v 坑了!!!!!
这是今天比赛的最水的一题,但是我看错题了。我以为不是链,结果gg了。
然后就很容易地发现了答案:

Ans=ΣRi=LΣnj=1a[j]min(c[i],c[j]) A n s = Σ i = L R Σ j = 1 n a [ j ] ∗ m i n ( c [ i ] , c [ j ] )

其中, c[i]=min(i,ni+1) c [ i ] = m i n ( i , n − i + 1 )
这可以看成是一个 nn n ∗ n 的格子,
然后怎么搞?
现在有什么条件和限制?
①既然都发现答案的式子了,并且询问的是一个区间 [l,r] [ l , r ] ,那么是不是可以设 f(i)=Σnj=1a[j]min(c[i],c[j]) f ( i ) = Σ j = 1 n a [ j ] ∗ m i n ( c [ i ] , c [ j ] ) ,求 Σri=lf(i) Σ i = l r f ( i )
②既然题目已经要求我们要支持 区间加的操作,可以 考虑增量
③由于 a[u]+=d a [ u ] + = d 等同于 a[nu+1]+=d a [ n − u + 1 ] + = d ,所以我们只用区间 [1,n+12] [ 1 , ⌊ n + 1 2 ⌋ ] 。如果超过了 n+12 ⌊ n + 1 2 ⌋ 则分开两步计算。
④为什么不考虑两个前缀和相减?因为在思考的过程中,已经将 i=1r i = 1 到 r 的各项系数减去 i=1l1 i = 1 到 l − 1 的,所以不用考虑这个方案了。(这只是一种想问题的方法,不必去理解)
所以考虑增量,部分分启发我们考虑增量为1的时候的 增量的函数图像
JZOJ 5695. 【GDSOI2018模拟4.26】链上二次求和_第1张图片
显然,图像分成三段。
第一段是一个 vu+1 v − u + 1 y=x y = x 的叠加。
第二段是一个二次函数。
第三段是一个常数。
然后在线段树上维护 x2 x 2 的系数, x x 的系数以及常数。
最后记得在运算的过程中,相同优先级的运算符是从左到右算的。这个害得我调了大半个小时……

代码

#include
#include
#include
#include
#include
#define N 200010
#define LL long long
#define mo 1000000007
#define P(a) putchar(a)
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note1{
    LL sum,a,b,c,la,lb,lc;
};note1 tr[N*4];
int i,j,k,l,r,n,m,u,op;
LL A,B,C,A1,B1,C1,opl,opr;
LL umi,Ny;
LL a[N],qz[N],c[N];
LL ans,lim;
int read(){
    int fh=1,rs=0;char ch;
    while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
    if(ch=='-')fh=-1,ch=getchar();
    while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
    return fh*rs;
}
void write(LL x){
    if(x>9)write(x/10);
    P(x%10+'0');
}
LL sum1(LL x){return x*(x+1)>>1;}
LL sum2(LL x){return x*(x+1)*((x<<1)+1)/6;}
void Add(LL &x,LL y){
    x=x+y>=mo?x+y-mo:x+y;
}
LL Min(LL x,LL y){return x<y?x:y;}
void add(int ps,LL l,LL r){
    LL temp=(A1*((sum2(r)-sum2(l-1)+mo)%mo)%mo+B1*(sum1(r)-sum1(l-1))%mo+C1*(r-l+1)%mo)%mo;
    Add(tr[ps].sum,temp);
    Add(tr[ps].la,A1);Add(tr[ps].lb,B1);Add(tr[ps].lc,C1);
}
void downld(int ps,int l,int r){
    if(!tr[ps].la&&!tr[ps].lb&&!tr[ps].lc)return;
    int wz=(l+r)>>1;
    A1=tr[ps].la;
    B1=tr[ps].lb;
    C1=tr[ps].lc;
    add(ps<<1,l,wz);
    add((ps<<1)|1,wz+1,r);
    tr[ps].la=tr[ps].lb=tr[ps].lc=0;
}
void update(int ps){
    tr[ps].sum=(tr[ps<<1].sum+tr[(ps<<1)|1].sum)%mo;
} 
void change(int ps,int l,int r,int x,int y){
    if(x>y)return;
    if(l==x&&r==y){
        A1=A;B1=B;C1=C;
        add(ps,l,r);
        return;
    }
    downld(ps,l,r);
    int wz=(l+r)>>1;
    if(y<=wz)change(ps<<1,l,wz,x,y);else
    if(x>wz)change((ps<<1)|1,wz+1,r,x,y);else{
        change(ps<<1,l,wz,x,wz);
        change((ps<<1)|1,wz+1,r,wz+1,y);
    }
    update(ps);
}
void modify(LL x,LL y){
    x=Min(x,n-x+1);
    y=Min(y,n-y+1);
    if(x>y)swap(x,y);
    A=0;B=u*(y-x+1)%mo;C=0;
    change(1,1,n,1,y);
    A=(-u*Ny%mo+mo)%mo;B=((-u*(-2*x+1)%mo+mo)%mo*Ny%mo+mo)%mo;C=((-u*(x*x%mo-x)%mo+mo)%mo*Ny)%mo;
    change(1,1,n,x+1,y);
    A=0;B=0;C=(((x+y)*(y-x+1)%mo)*u%mo)*Ny%mo;
    change(1,1,n,y+1,lim);
}
LL query(int ps,int l,int r,int x,int y){
    if(l==x&&r==y)return tr[ps].sum;
    downld(ps,l,r);
    int wz=(l+r)>>1;
    if(y<=wz)return query(ps<<1,l,wz,x,y);else
    if(x>wz)return query((ps<<1)|1,wz+1,r,x,y);else
    return (query(ps<<1,l,wz,x,wz)+query((ps<<1)|1,wz+1,r,wz+1,y))%mo;
} 
LL work(int x,int y){
    x=Min(x,n-x+1);
    y=Min(y,n-y+1);
    if(x>y)swap(x,y);
    return query(1,1,n,x,y);
}
int main(){
    freopen("sum.in","r",stdin);
    freopen("sum.out","w",stdout);
    Ny=500000004;
    n=read();m=read();
    fo(i,1,n)a[i]=read(),qz[i]=(qz[i-1]+a[i])%mo,c[i]=min(i,n-i+1);
    umi=0;
    lim=(n+1)>>1;
    fo(i,1,lim){
        umi=(umi+qz[n-i+1]-qz[i-1]+mo)%mo;
        A=0;B=0;C=umi;
        change(1,1,n,i,i);
    }
    while(m--){
        op=read();l=read();r=read();
        if(l>r)swap(l,r);
        if(op==1){
            u=read();
            if(l<=lim&&r>lim){
                modify(l,lim);
                modify(lim+1,r);
            }else modify(l,r);
        }else{
            ans=0;
            if(l<=lim&&r>lim)ans=(work(l,lim)+work(lim+1,r))%mo;
                        else ans=work(l,r);
            write(ans),P('\n');
        }
    }
    return 0;
}

你可能感兴趣的:(JZOJ 5695. 【GDSOI2018模拟4.26】链上二次求和)