P3707 [SDOI2017]相关分析

斯德哥尔摩

2018-05-28 22:19:58

Personal

[P3707 [SDOI2017]相关分析](https://www.luogu.org/problemnew/show/P3707) ### 先讲一讲题外话 这题恐怕是继[P4246 [SHOI2008]堵塞的交通](https://www.luogu.org/problemnew/show/P4246)之后,又一道超级毒瘤的线段树题,比那[P2572 [SCOI2010]序列操作](https://www.luogu.org/problemnew/show/P2572)恶心多了。。。 看我提交记录就知道此题有多毒。。。 SHOI那题我还没敢做~~(未见其题,先闻其毒。。。)~~,这题我调了两晚上,SCOI那题我大概一上午不到就1A了。。。 ### 步入正题 先分析**操作1**: 为了好解题,我们将$a$拆成两部分:$$a=\frac{s}{t}$$ 再把所有要用到的变量设一下:$$s1=\sum_{i=L}^{R}x_i,s2=\sum_{i=L}^{R}y_i,s3=\sum_{i=L}^{R}x_iy_i,s4=\sum_{i=L}^{R}x_i^2,len=R-L+1$$ 那么$s=\sum_{i=L}^{R}(x_i-\overline{x})(y_i-\overline{y}),t=\sum_{i=L}^{R}(x_i-\overline{x})^2$ 拆开$s$:$$s=\sum_{i=L}^{R}(x_iy_i-\overline{x}y_i-\overline{y}x_i+\overline{x}\overline{y})$$ $$=\sum_{i=L}^{R}x_iy_i-\overline{x}\sum_{i=L}^{R}y_i-\overline{y}\sum_{i=L}^{R}x_i+(R-L+1)\overline{x}\overline{y}$$ 又因为:$\overline{x}=\frac{1}{R-L+1}\sum_{i=L}^{R}x_i,\overline{y}=\frac{1}{R-L+1}\sum_{i=L}^{R}y_i$ 所以:$$s=s3-\frac{s1s2}{len}-\frac{s1s2}{len}+\frac{s1s2}{len}$$ 即:$$s=s3-\frac{s1s2}{len}$$ 同理对$t$:$t=\sum_{i=L}^{R}(x_i-\overline{x})^2$ 拆开:$$t=\sum_{i=L}^{R}x_i^2-2\overline{x}\sum_{i=L}^{R}x_i+(R-L+1)\overline{x}^2$$ 那么:$$t=s4-\frac{s1^2}{len}$$ 到此,$a$分析完毕。 那么怎么求$s1,s2,s3,s4$呢? 于是什么乱七八糟的数据结构都出来了——分块,树状数组,线段树,平衡树...... 但是这里选用**线段树**。 因为有修改操作。 **操作2**: 这个操作相对比较简单: $s1,s2$相对好做:$$s1+=S*(R-L+1)$$$$s2+=T*(R-L+1)$$ 但是那个$s3,s4$怎么维护? 我们想这样变:$xy->(x+S)(y+T),x^2->(x+S)^2$ 那就推式子啊,这个式子总比莫比乌斯反演好推吧。。。 于是:$$(x+S)(y+T)=xy+Sy+Tx+ST$$$$(x+S)^2=x^2+2xS+S^2$$ 所以: $$s3+=S* s2+T* s1+S* T* (R-L+1)$$ $$s4+=S*s1*2+S^2* (R-L+1)$$ 这个问题就这样愉快地解决了。。。 **操作3**: 这个操作比较烦。。。有前置要求。。。 前置要求:(小学奥数/高中必修) $$1^2+2^2+3^2+...+n^2=\frac{n(n+1)(2n+1)}{6}$$ $$1+2+3+...+n=\frac{n(n+1)}{2}$$ 对于赋值操作,我们可以拆开来:先将$[L,R]$中$x_i.y_i$全部改为$i$,再进行操作2。 定义两个函数: $$sum(n)=1+2+3+...+n=\frac{n(n+1)}{2}$$ $$mul(n)=1^2+2^2+3^2+...+n^2=\frac{n(n+1)(2n+1)}{6}$$ 那么: $$s1=s2=sum(R)-sum(L-1)=\frac{(L+R)(R-L+1)}{2}$$ $$s3=s4=mul(R)-mul(L-1)$$ 所以添加一个区间赋值的懒惰标记即可。 记得打懒惰标记时,区间加的标记要清$0$,因为赋值使得前面所有的添加操作都无效了。 ### 注意 这题理论上应该开$long\ long$,但是很奇怪的是开了$long\ long$反而过不了,而是开了位数不够大的$double$能过,也不知道出题人造了啥毁天灭地的数据。。。 话说$long\ double$表示不服呢(笑)。。。 所以这题都开$double$吧。。。 ### 结语 到此,这个题目总算是分析完了,推式子累死我,调程序累死我,神TM打字打得我更累我也是没话说。。。 ~~谁让出题人有一颗毒瘤的心呢。。。~~ 省选场上遇到这种题,两种选择:要么交暴力/优化后的暴力,要么调正解代码,但是**不能虚**!**千万不要虚**!**一定不能虚**!一虚便会紧张啊,慌啊啥的,考试最忌紧张!最忌慌!上去就是一个字——**怼**! 附代码: ```cpp #include<iostream> #include<algorithm> #include<cstdio> #define LSON rt<<1 #define RSON rt<<1|1 #define DATA1(x) a[x].data1 #define DATA2(x) a[x].data2 #define DATA3(x) a[x].data3 #define DATA4(x) a[x].data4 #define SIGN1(x) a[x].c1 #define SIGN2(x) a[x].c2 #define SIGN3(x) a[x].c3 #define LSIDE(x) a[x].l #define RSIDE(x) a[x].r #define WIDTH(x) (RSIDE(x)-LSIDE(x)+1) #define MAXN 100010 using namespace std; int n,m; double x[MAXN],y[MAXN]; struct Segment_Tree{ double data1,data2,data3,data4,c1,c2,c3; int l,r; }a[MAXN<<2]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } inline double sum(double l,double r){return ((l+r)*(r-l+1)/2.00);} inline double mul(double l,double r){return (r*(r+1)*(r*2.00+1)/6.00-(l-1)*l*(l*2.00-1)/6.00);}//化简了一下两个式子 inline void pushup(int rt){ DATA1(rt)=DATA1(LSON)+DATA1(RSON); DATA2(rt)=DATA2(LSON)+DATA2(RSON); DATA3(rt)=DATA3(LSON)+DATA3(RSON); DATA4(rt)=DATA4(LSON)+DATA4(RSON); } inline void pushdown(int rt){ if(LSIDE(rt)==RSIDE(rt))return; if(SIGN3(rt)){ SIGN3(LSON)=1;SIGN1(LSON)=SIGN2(LSON)=0; DATA1(LSON)=DATA2(LSON)=sum(LSIDE(LSON),RSIDE(LSON)); DATA3(LSON)=DATA4(LSON)=mul(LSIDE(LSON),RSIDE(LSON)); SIGN3(RSON)=1;SIGN1(RSON)=SIGN2(RSON)=0; DATA1(RSON)=DATA2(RSON)=sum(LSIDE(RSON),RSIDE(RSON)); DATA3(RSON)=DATA4(RSON)=mul(LSIDE(RSON),RSIDE(RSON)); SIGN3(rt)=0; } if(SIGN1(rt)||SIGN2(rt)){ SIGN1(LSON)+=SIGN1(rt);SIGN2(LSON)+=SIGN2(rt); DATA3(LSON)+=SIGN1(rt)*DATA2(LSON)+SIGN2(rt)*DATA1(LSON)+SIGN1(rt)*SIGN2(rt)*WIDTH(LSON); DATA4(LSON)+=SIGN1(rt)*DATA1(LSON)*2+SIGN1(rt)*SIGN1(rt)*WIDTH(LSON); DATA1(LSON)+=SIGN1(rt)*WIDTH(LSON);DATA2(LSON)+=SIGN2(rt)*WIDTH(LSON); SIGN1(RSON)+=SIGN1(rt);SIGN2(RSON)+=SIGN2(rt); DATA3(RSON)+=SIGN1(rt)*DATA2(RSON)+SIGN2(rt)*DATA1(RSON)+SIGN1(rt)*SIGN2(rt)*WIDTH(RSON); DATA4(RSON)+=SIGN1(rt)*DATA1(RSON)*2+SIGN1(rt)*SIGN1(rt)*WIDTH(RSON); DATA1(RSON)+=SIGN1(rt)*WIDTH(RSON);DATA2(RSON)+=SIGN2(rt)*WIDTH(RSON); SIGN1(rt)=SIGN2(rt)=0; } } void buildtree(int l,int r,int rt){ int mid; LSIDE(rt)=l; RSIDE(rt)=r; SIGN1(rt)=SIGN2(rt)=SIGN3(rt)=0; if(l==r){ DATA1(rt)=x[l];DATA2(rt)=y[l];DATA3(rt)=x[l]*y[l];DATA4(rt)=x[l]*x[l]; return; } mid=l+r>>1; buildtree(l,mid,LSON); buildtree(mid+1,r,RSON); pushup(rt); } void update_add(int l,int r,double c,double d,int rt){ int mid; if(l<=LSIDE(rt)&&RSIDE(rt)<=r){ SIGN1(rt)+=c;SIGN2(rt)+=d; DATA3(rt)+=c*DATA2(rt)+d*DATA1(rt)+c*d*WIDTH(rt);DATA4(rt)+=c*DATA1(rt)*2+c*c*WIDTH(rt); DATA1(rt)+=c*WIDTH(rt);DATA2(rt)+=d*WIDTH(rt); return; } pushdown(rt); mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)update_add(l,r,c,d,LSON); if(mid<r)update_add(l,r,c,d,RSON); pushup(rt); } void update_all(int l,int r,int rt){ int mid; if(l<=LSIDE(rt)&&RSIDE(rt)<=r){ SIGN3(rt)=1;SIGN1(rt)=SIGN2(rt)=0; DATA1(rt)=DATA2(rt)=sum(LSIDE(rt),RSIDE(rt)); DATA3(rt)=DATA4(rt)=mul(LSIDE(rt),RSIDE(rt)); return; } pushdown(rt); mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)update_all(l,r,LSON); if(mid<r)update_all(l,r,RSON); pushup(rt); } double query_one(int l,int r,int rt){ int mid; double ans=0.00; if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA1(rt); pushdown(rt); mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)ans+=query_one(l,r,LSON); if(mid<r)ans+=query_one(l,r,RSON); return ans; } double query_two(int l,int r,int rt){ int mid; double ans=0.00; if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA2(rt); pushdown(rt); mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)ans+=query_two(l,r,LSON); if(mid<r)ans+=query_two(l,r,RSON); return ans; } double query_three(int l,int r,int rt){ int mid; double ans=0.00; if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA3(rt); pushdown(rt); mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)ans+=query_three(l,r,LSON); if(mid<r)ans+=query_three(l,r,RSON); return ans; } double query_four(int l,int r,int rt){ int mid; double ans=0.00; if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA4(rt); pushdown(rt); mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)ans+=query_four(l,r,LSON); if(mid<r)ans+=query_four(l,r,RSON); return ans; } void work(){ int f,l,r; double s,t; while(m--){ f=read();l=read();r=read(); if(f==1){ double s1=query_one(l,r,1),s2=query_two(l,r,1),len=r-l+1; double s3=query_three(l,r,1),s4=query_four(l,r,1); s=s3-s1*s2/len;t=s4-s1*s1/len; printf("%.10lf\n",s/t); } if(f==2){ scanf("%lf%lf",&s,&t); update_add(l,r,s,t,1); } if(f==3){ scanf("%lf%lf",&s,&t); update_all(l,r,1); update_add(l,r,s,t,1); } } } void init(){ n=read();m=read(); for(int i=1;i<=n;i++)scanf("%lf",&x[i]); for(int i=1;i<=n;i++)scanf("%lf",&y[i]); buildtree(1,n,1); } int main(){ init(); work(); return 0; } ```