bzoj4821 && luogu3707 SDOI2017相關分析(線段樹,數學)
題目大意
給定n個元素的數列,每一個元素有x和y兩種元素,現在有三種操作:
\(1\ L\ R\)
設\(xx\)為\([l,r]\)的元素的\(x_i\)的平均值,\(yy\)同理
求 \(\frac{\sum_{i=L}^R(x_i-xx)(y_i-yy)} {\sum_{i=L}^R(xi-xx)^2}\)
\(2\ L\ R\ S\ T\\)
將\([L,R]\)中的每個元素的\(x_i\)+S,\(y_i\)+T
$3 L R S T $
對於\([L,R]\)中的每個元素,設其為\(i\),將它的\(x_i\)改為\(S+i\),\(y_i\)改為\(T+i\)
其中\(n \le 100000 , m\le100000\)
要是回答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]\)
\(\sum (x_i+S)\) = \(\sum x_i +(r-l+1)\times S\)
\(\sum (y_i+T)\) = \(\sum y_i +(r-l+1)\times T\)
\(\sum(x_i+S)^2\) = \(\sum x_i^2 + 2\times \sum x_i \times S +S^2\)
\(\sum(x_I+S)(y_i+T)\) = \(\sum x_i y_i\) + \(T\times \sum x_i\)+\(S\times \sum y_i\)+\((r-l+1)\times T \times 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.把\(x_i\)和\(y_i\)修改成\(i\)
2.將\(x_i+S\),\(y_i+T\)
那麽我們思考,全部覆蓋成\(i\)應該怎麽做呢QwQ
首先,一旦一個區間被打了覆蓋標記,那麽之前的add 的標記就需要全部清空
我們會發現修改完的序列是這樣的\(1,2,3,4,5.....\)
而\(\sum_{i=1}^n i^2 = \frac {n(n+1)(2n+1)}{6}\)
所以,對於一個區間\([l,r]\)
\(\sum x_i = \sum y_i = \frac{(r-l+1)(r+l)}{2}\)
\(\sum x_i^2 = \sum x_i y_i = \frac {(r+1)r(2r+1)}{6} - \frac{l(l-1)(2l-1)}{6}\)
在下傳標記的時候,當把覆蓋標記下傳完之後,把傳到的那兩個子區間 的 \(add\) 清空
一定記得把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共勉
bzoj4821 && luogu3707 SDOI2017相關分析(線段樹,數學)