4 条题解
-
1
关于方差有这样一个公式,高中数学会学到(不过提高组的要求是掌握高中数学知识吧):
如果 个数的方差为 ,平均数为 , 个数的方差为 ,平均数为 ,这 个数的平均数为 ,设方差为 ,则有如下公式:
这个公式可以由最基本的公式(题目给的)推出,有了这个公式,就可以由方差推方差了,好处在于不用维护平方和,而且方差表示数据波动程度,意味着给区间加,则这段区间的方差不变!但是坏处,查询函数比较难写。
所以我们要在线段树上维护两个信息:区间和和方差,因为平均数可以由区间和得出所以不用维护,当然也可以维护个平均数可能能更方便。区间合并方差可以用上述公式这样写:
tr[rt][2] = ((mid-l+1) * (tr[rt<<1][2] + (tr[rt][1] - tr[rt<<1][1]) * (tr[rt][1] - tr[rt<<1][1])) + (r-mid) * (tr[rt<<1|1][2] + (tr[rt][1] - tr[rt<<1|1][1]) * (tr[rt][1] - tr[rt<<1|1][1]))) * 1.0 / (r-l+1);
下面就说一下写查询是要注意的地方:我们知道,线段树查询原理是吧一个大区间拆成许多小区间,此题这种写法难在确定子区间长度,既不是简单的当前子区间长度,也不是整个问题的长度。如果当前子区间要一分为二,设当前子区间左边界为 ,右边界为 ,整个问题左边界为 ,右边界为 ,则当前区间长度为 。还是按 取,但左右端点分别为 。当然,要是当前区间中只找左边或只找右边就直接返回即可。
code:
#include <bits/stdc++.h> using namespace std; #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 const int MAXN = 1e5 + 5; double tr[MAXN<<2][3], add[MAXN<<2], a[MAXN]; //0sum 1avg 2fangcha void push_up(int l, int r, int rt) { int mid = (l + r) / 2; tr[rt][0] = tr[rt<<1][0] + tr[rt<<1|1][0]; tr[rt][1] = tr[rt][0] / (r-l+1); tr[rt][2] = ((mid-l+1) * (tr[rt<<1][2] + (tr[rt][1] - tr[rt<<1][1]) * (tr[rt][1] - tr[rt<<1][1])) + (r-mid) * (tr[rt<<1|1][2] + (tr[rt][1] - tr[rt<<1|1][1]) * (tr[rt][1] - tr[rt<<1|1][1]))) * 1.0 / (r-l+1); } void push_down(int l, int r, int rt) { if (!add[rt]) return; int mid = (l + r) / 2; tr[rt<<1][0] += add[rt] * (mid-l+1); tr[rt<<1|1][0] += add[rt] * (r-mid); tr[rt<<1][1] += add[rt]; tr[rt<<1|1][1] += add[rt]; add[rt<<1] += add[rt]; add[rt<<1|1] += add[rt]; add[rt] = 0; } void build(int l, int r, int rt) { if (l == r) { tr[rt][0] = tr[rt][1] = a[l]; tr[rt][2] = 0; } else { int mid = (l + r) / 2; build(lson); build(rson); push_up(l, r, rt); } } void update(int L, int R, double x, int l, int r, int rt) { if (L <= l && r <= R) { tr[rt][0] += (r-l+1) * x; tr[rt][1] += x; add[rt] += x; return; } int mid = (l + r) / 2; push_down(l, r, rt); if (mid >= L) update(L, R, x, lson); if (mid < R) update(L, R, x, rson); push_up(l, r, rt); } double query(int L, int R, int l, int r, int rt, int t) { if (L <= l && r <= R) { if (t == 1) return tr[rt][0]; if (t == 2) return tr[rt][1]; if (t == 3) return tr[rt][2]; } int mid = (l + r) / 2; push_down(l, r, rt); double sum = 0; if (mid >= L) sum += query(L, R, lson, 1); if (mid < R) sum += query(L, R, rson, 1); if (t == 1) { return sum; } if (t == 2) { L = max(L, l); R = min(R, r); return sum / (R-L + 1); } if (t == 3) { double s1 = -11451412, s2 = -1145141, x1 = 0, x2 = 0, x = sum / (min(R, r)-max(L, l)+1); bool f1 = 0, f2 = 0; if (mid >= L) s1 = query(L, R, lson, 3), x1 = query(L, R, lson, 2), f1 = 1; if (mid < R) s2 = query(L, R, rson, 3), x2 = query(L, R, rson, 2), f2 = 1; if (f1 && f2) { L = max(L, l); R = min(R, r); return ((mid-L+1) * (s1 + (x1 - x) * (x1 - x)) + (R-mid) * (s2 + (x2 - x) * (x2-x))) * 1.0 / (R-L+1); } else if (f1) { return s1; } else if (f2) { return s2; } } } int main() { int n, m; cin >> n >> m; for (int i = 1; i <= n; i++) cin >> a[i]; build(1, n, 1);//cout << tr[1][0] << endl; for (int i = 1; i <= m; i++) { int op, x, y; double k; cin >> op >> x >> y; if (op == 1) { cin >> k; update(x, y, k, 1, n, 1); } else { printf("%.4lf\n",query(x, y, 1, n, 1, op)); } } return 0; }
-
1
#include <bits/stdc++.h> #define ll long long #define mod 1000000007 #define mid (l+r>>1) using namespace std; const int N=100005; int n,m,k,s,t; double a[N],ans,ans2; struct node{ double sum[N<<2],tag[N<<2],sum2[N<<2]; // sum:和 tag:标记 sum2:平方和 void update(int p){ sum[p]=sum[p<<1]+sum[p<<1|1]; sum2[p]=sum2[p<<1]+sum2[p<<1|1]; } void down(int p,int l,int r,double k){ // 区间[l,r]整体增加k sum2[p]+=2*k*sum[p]+(r-l+1)*k*k; sum[p]+=(r-l+1)*k; //注意要先更新sum2,也就是区间的平方的和,再更新sum,也就是区间和 tag[p]+=k; } void build(int p,int l,int r){ if (l==r) { sum[p]=a[l]; sum2[p]=a[l]*a[l]; return; } build(p<<1,l,mid); build(p<<1|1,mid+1,r); update(p); } void change(int p,int l,int r,int s,int t,double k){ //[s,t] 增加k if (l>=s && r<=t){ down(p,l,r,k); return; } if (tag){ down(p<<1,l,mid,tag[p]); down(p<<1|1,mid+1,r,tag[p]); tag[p]=0; } if (s<=mid) change(p<<1,l,mid,s,t,k); if (t>mid) change(p<<1|1,mid+1,r,s,t,k); update(p); } void query(int p,int l,int r,int s,int t){ //查询[s,t]的和 以及平方和 if (l>=s && r<=t){ ans+=sum[p]; ans2+=sum2[p]; return; } if (tag){ down(p<<1,l,mid,tag[p]); down(p<<1|1,mid+1,r,tag[p]); tag[p]=0; } if (s<=mid) query(p<<1,l,mid,s,t); if (t>mid) query(p<<1|1,mid+1,r,s,t); } }T; int main(){ freopen("in.txt","r",stdin); cin>>n>>m; for (int i=1;i<=n;++i) cin>>a[i]; T.build(1,1,n); int opt,l,r;double x; for (int i=1;i<=m;++i){ cin>>opt>>l>>r; if (opt==1){ cin>>x; T.change(1,1,n,l,r,x); } if (opt==2){ ans=ans2=0; T.query(1,1,n,l,r); printf("%.4f\n",ans/(r-l+1)); } if (opt==3){ ans=ans2=0; T.query(1,1,n,l,r); ans/=(r-l+1); ans2/=(r-l+1); printf("%.4f\n",-ans*ans+ans2); } } }
-
0
不考虑操作的时候,就是线段树模板(一)。
再有了操作,需要求方差的时候,需要推式子,详细过程在上面题解已经展示,然后维护一下区间和,以及区间平方和即可。
注意该定义
double
的不要写错。#include <bits/stdc++.h> #define ll long long #define ls p << 1 #define rs p << 1 | 1 using namespace std; const int N = 1e5 + 5; int n, m; double a[N]; struct segment_tree { int l, r; double sum, res, tag;//和,平方和,懒标记 } t[N << 2]; void push_up(int p) { t[p].sum = t[ls].sum + t[rs].sum; t[p].res = t[ls].res + t[rs].res; } void build(int p, int l, int r) { t[p].l = l, t[p].r = r; if (l == r) { t[p].sum = a[l]; t[p].res = a[l] * a[l]; return ; } int mid = l + r >> 1; build(ls, l, mid), build(rs, mid + 1, r); push_up(p); } void push_down(int p) { int mid = t[p].l + t[p].r >> 1; if (t[p].tag) { t[ls].res += (mid - t[ls].l + 1) * t[p].tag * t[p].tag + 2 * t[p].tag * t[ls].sum; t[rs].res += (t[rs].r - mid) * t[p].tag * t[p].tag + 2 * t[p].tag * t[rs].sum; t[ls].sum += (mid - t[ls].l + 1) * t[p].tag; t[rs].sum += (t[rs].r - mid) * t[p].tag; t[ls].tag += t[p].tag; t[rs].tag += t[p].tag; t[p].tag = 0; } } void change(int p, int l, int r, double k) { if (l <= t[p].l && r >= t[p].r) { t[p].tag += k; t[p].res += (t[p].r - t[p].l + 1) * k * k + 2 * k * t[p].sum; t[p].sum += (t[p].r - t[p].l + 1) * k; return ; } push_down(p); int mid = t[p].l + t[p].r >> 1; if (l <= mid) change(ls, l, r, k); if (r > mid) change(rs, l, r, k); push_up(p); } double query1(int p, int l, int r) { if (l <= t[p].l && r >= t[p].r) { return t[p].sum; } double ans = 0; push_down(p); int mid = t[p].l + t[p].r >> 1; if (l <= mid) ans += query1(ls, l, r); if (r > mid) ans += query1(rs, l, r); return ans; } double query2(int p, int l, int r) { if (l <= t[p].l && r >= t[p].r) { return t[p].res; } double ans = 0; push_down(p); int mid = t[p].l + t[p].r >> 1; if (l <= mid) ans += query2(ls, l, r); if (r > mid) ans += query2(rs, l, r); return ans; } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m; for (int i = 1; i <= n; i++) cin >> a[i]; build(1, 1, n); while (m--) { int op, x, y; double k; cin >> op >> x >> y; if (op == 1) { cin >> k; change(1, x, y, k); } else if (op == 2) { double ans = query1(1, x, y); cout << fixed << setprecision(4) << ans / (y - x + 1) << "\n"; } else { double ans1 = query1(1, x, y); double avg1 = ans1 / (y - x + 1); double ans2 = query2(1, x, y); double avg2 = ans2 / (y - x + 1); cout << fixed << setprecision(4) << -avg1 * avg1 + avg2 << "\n"; } } return 0; }
-
0
部分代码
struct node { double lazy,sum; } t1[N<<2],t2[N<<2]; double a[N]; inline void Push_up(int rt) { t1[rt].sum=t1[rt<<1].sum+t1[rt<<1 | 1].sum; t2[rt].sum=t2[rt<<1].sum+t2[rt<<1 | 1].sum; } void build(int rt,int l,int r) { if (l==r) { t1[rt].sum=a[l]; t2[rt].sum=a[l]*a[l]; return; } build(Lson); build(Rson); Push_up(rt); } inline void Push_down(int rt,int l,int r) { if (!(t1[rt].lazy || t2[rt].lazy)) return ; t2[rt<<1].lazy+=t2[rt].lazy; t2[rt<<1].sum+=t1[rt<<1].sum*(t2[rt].lazy*2)+(mid-l+1)*(t2[rt].lazy*t2[rt].lazy); t2[rt<<1 | 1].lazy+=t2[rt].lazy; t2[rt<<1 | 1].sum+=t1[rt<<1 | 1].sum*(t2[rt].lazy*2)+(r-mid)*(t2[rt].lazy*t2[rt].lazy); t2[rt].lazy=0; t1[rt<<1].lazy+=t1[rt].lazy; t1[rt<<1].sum+=(mid-l+1)*t1[rt].lazy; t1[rt<<1 | 1].lazy+=t1[rt].lazy; t1[rt<<1 | 1].sum+=(r-mid)*t1[rt].lazy; t1[rt].lazy=0; } double Query(int rt,int l,int r,int L,int R,int x) { if (L<=l && r<=R) return x==1 ? t1[rt].sum:t2[rt].sum; double ans=0; Push_down(rt,l,r); if (L<=mid) ans+=Query(Lson,L,R,x); if (R>mid) ans+=Query(Rson,L,R,x); return ans; } void Update(int rt,int l,int r,int L,int R,double v) { if (L<=l && r<=R) { t2[rt].lazy+=v; t2[rt].sum+=t1[rt].sum*(v*2)+len*(v*v); t1[rt].lazy+=v; t1[rt].sum+=len*v; return ; } Push_down(rt,l,r); if (L<=mid) Update(Lson,L,R,v); if (R>mid) Update(Rson,L,R,v); Push_up(rt); } inline void init() { // freopen("data.in","r",stdin); // freopen("a.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) scanf("%lf",&a[i]); build(1,1,n); while(m--) { int x,a,b; scanf("%d%d%d",&x,&a,&b); if (x==1) { double c; scanf("%lf",&c); Update(1,1,n,a,b,c); } if (x==2) printf("%.4lf\n",Query(1,1,n,a,b,1)/((b-a+1)*1.0)); if (x==3) { double cnt=(b-a+1)*1.0; double ans=Query(1,1,n,a,b,2)/cnt,x_ba=Query(1,1,n,a,b,1)/cnt; double ans2=x_ba*x_ba; ans-=ans2; printf("%.4lf\n",ans); } } }
- 1
信息
- ID
- 221
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 4
- 标签
- 递交数
- 73
- 已通过
- 33
- 上传者