bzoj4821 && luogu3707 SDOI2017相关分析(线段树,数学)

题目大意

给定n个元素的数列,每一个元素有x和y两种元素,现在有三种操作:

1 L R 1   L   R
xx x x [l,r] [ l , r ] 的元素的 xi x i 的平均值, yy y y 同理

Ri=L(xixx)(yiyy)Ri=L(xixx)2 ∑ i = L R ( x i − x x ) ( y i − y y ) ∑ i = L R ( x i − x x ) 2

2 L R S T  2   L   R   S   T  
[L,R] [ L , R ] 中的每个元素的 xi x i +S, yi y i +T

3 L R S T  3   L   R   S   T  
对于 [L,R] [ L , R ] 中的每个元素,设其为 i i ,将它的xi改为 S+i S + i yi y i 改为 T+i T + i

其中 n100000m100000 n ≤ 100000 , m ≤ 100000

要是回答1询问的话,首先需要将线段树维护一个每个元素的x的平方的和、x的和、y的和,以及每一个元素x*y的和

那么我们就开一个struct来记录这些值

struct Node{
    double sx,sy,sqr,xy,sum;
};

up数组的话,和普通的线段树差不多

void up(int root)
{
    f[root].sx=f[2*root].sx+f[2*root+1].sx;
    f[root].sy=f[2*root].sy+f[2*root+1].sy;
    f[root].sqr=f[2*root].sqr+f[2*root+1].sqr;
    f[root].xy=f[2*root].xy+f[2*root+1].xy;
}

对于pushdown,我们首先考虑增加操作,对于一个区间 [l,r] [ l , r ]
(xi+S) ∑ ( x i + S ) = xi+(rl+1)×S ∑ x i + ( r − l + 1 ) × S
(yi+T) ∑ ( y i + T ) = yi+(rl+1)×T ∑ y i + ( r − l + 1 ) × T
(xi+S)2 ∑ ( x i + S ) 2 = x2i+2×xi×S+S2 ∑ x i 2 + 2 × ∑ x i × S + S 2
(xI+S)(yi+T) ∑ ( x I + S ) ( y i + T ) = xiyi ∑ x i y i + T×xi T × ∑ x i + S×yi S × ∑ y i + (rl+1)×T×S ( r − l + 1 ) × T × S

经过一波操作,就可以直接在原来的基础上进行加法的操作了
注意!!! 先更新乘法那些,再更新加法

if (add[root].x || add[root].y)
    {
       add[2*root].x+=add[root].x;
       add[2*root+1].x+=add[root].x;
       add[2*root].y+=add[root].y;
       add[2*root+1].y+=add[root].y;
       f[2*root].sqr+=(mid-l+1)*add[root].x*add[root].x+2*f[2*root].sx*add[root].x;
       f[2*root+1].sqr+=(r-mid)*add[root].x*add[root].x+2*f[2*root+1].sx*add[root].x;
       f[2*root].xy+=(mid-l+1)*add[root].x*add[root].y+f[2*root].sx*add[root].y+f[2*root].sy*add[root].x;   
       f[2*root+1].xy+=(r-mid)*add[root].x*add[root].y+f[2*root+1].sx*add[root].y+f[2*root+1].sy*add[root].x; 
       f[2*root].sx+=(mid-l+1)*add[root].x;
       f[2*root+1].sx+=(r-mid)*add[root].x;
       f[2*root].sy+=(mid-l+1)*add[root].y;
       f[2*root+1].sy+=(r-mid)*add[root].y;      
       add[root].x=0;
       add[root].y=0;
    }

那么那么那么,对于有覆盖操作的的呢?
我们可以这么想,把覆盖分解成两步
1.把 xi x i yi y i 修改成 i i
2.将xi+S yi+T y i + T

那么我们思考,全部覆盖成 i i 应该怎么做呢QwQ

首先,一旦一个区间被打了覆盖标记,那么之前的add 的标记就需要全部清空

我们会发现修改完的序列是这样的1,2,3,4,5.....

ni=1i2=n(n+1)(2n+1)6 ∑ i = 1 n i 2 = n ( n + 1 ) ( 2 n + 1 ) 6

所以,对于一个区间 [l,r] [ l , r ]

xi=yi=(rl+1)(r+l)2 ∑ x i = ∑ y i = ( r − l + 1 ) ( r + l ) 2

x2i=xiyi=(r+1)r(2r+1)6l(l1)(2l1)6 ∑ x i 2 = ∑ x i y i = ( r + 1 ) r ( 2 r + 1 ) 6 − l ( l − 1 ) ( 2 l − 1 ) 6

在下传标记的时候,当把覆盖标记下传完之后,把传到的那两个子区间 的 add a d d 清空
一定记得把add的标记清空!!!!!!

if (flag[root])
    {
        flag[2*root+1]=1;flag[2*root]=1;flag[root]=0;add[2*root].x=add[2*root].y=add[2*root+1].x=add[2*root+1].y=0;
        f[2*root].sx=(l+mid)*(mid-l+1)/2;
        f[2*root].sy=(l+mid)*(mid-l+1)/2;
        f[2*root+1].sx=(r+mid+1)*(r-mid-1+1)/2;
        f[2*root+1].sy=(r+mid+1)*(r-mid-1+1)/2;
        f[2*root].xy=f[2*root].sqr=(mid+1)*mid*(2*mid+1)/6-(l-1)*l*(2*l-1)/6;
        f[2*root+1].xy=f[2*root+1].sqr=(r+1)*r*(2*r+1)/6-(mid)*(mid+1)*(2*mid+1)/6;
    }

build也和普通的线段树没什么区别

void build(int root,int l,int r)
{
    if (l==r)
    {
        f[root].sx=a[l].x;
        f[root].sy=a[l].y;
        f[root].xy=a[l].x*a[l].y;
        f[root].sqr=a[l].x*a[l].x;
        return;
    }
    int mid = (l+r) >> 1;
    build(2*root,l,mid);
    build(2*root+1,mid+1,r);
    up(root);
}

再就是update和change了 QwQ我的做法是把区间加和区间赋值分开写,其实和pushdown的操作基本上完全一样
直接上代码了

void update(int root,int l,int r,int x,int y,double px,double py)
{
    if (x<=l && r<=y)
    {
        add[root].x+=px;
        add[root].y+=py;
        f[root].xy+=(double)(r-l+1)*px*py+(double)f[root].sx*py+(double)f[root].sy*px;
        f[root].sqr+=(double)(r-l+1)*px*px+(double)f[root].sx*px*2;
        f[root].sx+=(double)(r-l+1)*px;
        f[root].sy+=(double)(r-l+1)*py;
        return;
    }
    pushdown(root,l,r);
    int mid = (l+r) >> 1;
    if (x<=mid) update(2*root,l,mid,x,y,px,py);
    if (y>mid) update(2*root+1,mid+1,r,x,y,px,py);
    up(root);
}

void change(int root,int l,int r,int x,int y)
{
    if (x<=l && r<=y)
    {
        flag[root]=1;
        f[root].sy=f[root].sx=(double)(r+l)*(double)(r-l+1)/2.0;
        f[root].xy=f[root].sqr=(double)(r+1)*(double)r*(double)(2*r+1)/6.0-(double)(l-1)*(double)l*(double)(2*l-1)/6.0;
        add[root].x=add[root].y=0;
        return;
    }
    pushdown(root,l,r);
    int mid = (l+r) >> 1;
    if (x<=mid) change(2*root,l,mid,x,y);
    if (y>mid) change(2*root+1,mid+1,r,x,y);
    up(root);
}

求答案的部分就不放了
其他的emmmm 也很裸 直接上全部的代码

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<map>
#include<vector>

using namespace std;

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

const int maxn = 3e5+1e2;

struct Node{
    double sx,sy,sqr,xy,sum;
};

struct po{
    double x,y;
};

Node f[4*maxn];
po add[4*maxn],a[maxn];
bool flag[4*maxn];
int n,m;

void up(int root)
{
    f[root].sx=f[2*root].sx+f[2*root+1].sx;
    f[root].sy=f[2*root].sy+f[2*root+1].sy;
    f[root].sqr=f[2*root].sqr+f[2*root+1].sqr;
    f[root].xy=f[2*root].xy+f[2*root+1].xy;
//  f[root].sum=f[2*root].sum+f[2*root+1].sum;  
}

void pushdown(int root,int ll,int rr)
{
    double mid = (ll+rr)/2;
    double l=ll,r=rr;
    if (flag[root])
    {
        flag[2*root+1]=1;flag[2*root]=1;flag[root]=0;add[2*root].x=add[2*root].y=add[2*root+1].x=add[2*root+1].y=0;
        f[2*root].sx=(l+mid)*(mid-l+1)/2;
        f[2*root].sy=(l+mid)*(mid-l+1)/2;
        f[2*root+1].sx=(r+mid+1)*(r-mid-1+1)/2;
        f[2*root+1].sy=(r+mid+1)*(r-mid-1+1)/2;
        f[2*root].xy=f[2*root].sqr=(mid+1)*mid*(2*mid+1)/6-(l-1)*l*(2*l-1)/6;
        f[2*root+1].xy=f[2*root+1].sqr=(r+1)*r*(2*r+1)/6-(mid)*(mid+1)*(2*mid+1)/6;
    }   
    if (add[root].x || add[root].y)
    {
       add[2*root].x+=add[root].x;
       add[2*root+1].x+=add[root].x;
       add[2*root].y+=add[root].y;
       add[2*root+1].y+=add[root].y;
       f[2*root].sqr+=(mid-l+1)*add[root].x*add[root].x+2*f[2*root].sx*add[root].x;
       f[2*root+1].sqr+=(r-mid)*add[root].x*add[root].x+2*f[2*root+1].sx*add[root].x;
       f[2*root].xy+=(mid-l+1)*add[root].x*add[root].y+f[2*root].sx*add[root].y+f[2*root].sy*add[root].x;   
       f[2*root+1].xy+=(r-mid)*add[root].x*add[root].y+f[2*root+1].sx*add[root].y+f[2*root+1].sy*add[root].x; 
       f[2*root].sx+=(mid-l+1)*add[root].x;
       f[2*root+1].sx+=(r-mid)*add[root].x;
       f[2*root].sy+=(mid-l+1)*add[root].y;
       f[2*root+1].sy+=(r-mid)*add[root].y;      
       add[root].x=0;
       add[root].y=0;
    }
}

void build(int root,int l,int r)
{
    if (l==r)
    {
        f[root].sx=a[l].x;
        f[root].sy=a[l].y;
        f[root].xy=a[l].x*a[l].y;
        f[root].sqr=a[l].x*a[l].x;
        return;
    }
    int mid = (l+r) >> 1;
    build(2*root,l,mid);
    build(2*root+1,mid+1,r);
    up(root);
}

void update(int root,int l,int r,int x,int y,double px,double py)
{
    if (x<=l && r<=y)
    {
        add[root].x+=px;
        add[root].y+=py;
        f[root].xy+=(double)(r-l+1)*px*py+(double)f[root].sx*py+(double)f[root].sy*px;
        f[root].sqr+=(double)(r-l+1)*px*px+(double)f[root].sx*px*2;
        f[root].sx+=(double)(r-l+1)*px;
        f[root].sy+=(double)(r-l+1)*py;
        return;
    }
    pushdown(root,l,r);
    int mid = (l+r) >> 1;
    if (x<=mid) update(2*root,l,mid,x,y,px,py);
    if (y>mid) update(2*root+1,mid+1,r,x,y,px,py);
    up(root);
}

void change(int root,int l,int r,int x,int y)
{
    if (x<=l && r<=y)
    {
        flag[root]=1;
        f[root].sy=f[root].sx=(double)(r+l)*(double)(r-l+1)/2.0;
        f[root].xy=f[root].sqr=(double)(r+1)*(double)r*(double)(2*r+1)/6.0-(double)(l-1)*(double)l*(double)(2*l-1)/6.0;
        add[root].x=add[root].y=0;
        return;
    }
    pushdown(root,l,r);
    int mid = (l+r) >> 1;
    if (x<=mid) change(2*root,l,mid,x,y);
    if (y>mid) change(2*root+1,mid+1,r,x,y);
    up(root);
}

double queryxy(int root,int l,int r,int x,int y)
{
    if (x<=l && r<=y)
    {
        return f[root].xy;
    }
    pushdown(root,l,r);
    int mid = (l+r)>>1;
    double ans=0;
    if (x<=mid) ans+=queryxy(2*root,l,mid,x,y);
    if (y>mid) ans+=queryxy(2*root+1,mid+1,r,x,y);
    return ans;
}

double querysqr(int root,int l,int r,int x,int y)
{
    if (x<=l && r<=y)
    {
        return f[root].sqr;
    }
    pushdown(root,l,r);
    int mid = (l+r)>>1;
    double ans=0;
    if (x<=mid) ans+=querysqr(2*root,l,mid,x,y);
    if (y>mid) ans+=querysqr(2*root+1,mid+1,r,x,y);
    return ans;
}

double querysx(int root,int l,int r,int x,int y)
{
    if (x<=l && r<=y)
    {
        return f[root].sx;
    }
    pushdown(root,l,r);
    int mid = (l+r)>>1;
    double ans=0;
    if (x<=mid) ans+=querysx(2*root,l,mid,x,y);
    if (y>mid) ans+=querysx(2*root+1,mid+1,r,x,y);
    return ans;
}

double querysy(int root,int l,int r,int x,int y)
{
    if (x<=l && r<=y)
    {
        return f[root].sy;
    }
    pushdown(root,l,r);
    int mid = (l+r)>>1;
    double ans=0;
    if (x<=mid) ans+=querysy(2*root,l,mid,x,y);
    if (y>mid) ans+=querysy(2*root+1,mid+1,r,x,y);
    return ans;
}

double solve(int l,int r)
{
   double ax = querysx(1,1,n,l,r)/(double)(r-l+1);
   double ay = querysy(1,1,n,l,r)/(double)(r-l+1);
   update(1,1,n,l,r,-ax,-ay);
   double ans = queryxy(1,1,n,l,r)/querysqr(1,1,n,l,r);
   update(1,1,n,l,r,ax,ay);
   return ans;
}

int main()
{
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;i++) scanf("%lf",&a[i].x);
  for (int i=1;i<=n;i++) scanf("%lf",&a[i].y);
  build(1,1,n);
  for (int i=1;i<=m;i++)
  {
     int opt;
     int x,y;
     double px,py;
     opt=read();
     if (opt==1)
     {
         x=read(),y=read();printf("%.10lf\n",solve(x,y));
       }
     if (opt==2)
     {
        x=read(),y=read();
        scanf("%lf%lf",&px,&py);
        update(1,1,n,x,y,px,py);
     }
     if (opt==3)
     {
        x=read(),y=read();
        scanf("%lf%lf",&px,&py);
        change(1,1,n,x,y);
        update(1,1,n,x,y,px,py);
     }
  }
  return 0;
}

QwQ共勉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值