「ch52」还教室
还教室
还记得 NOIP 2012 提高组 Day2 中的借教室吗?时光飞逝,光阴荏苒,两年 过去了,曾经借教室的同学们纷纷归还自己当初租借的教室。请你来解决类似于 借教室的另一个问题。
「问题描述」
在接受借教室请求的 n 天中,第 i 天剩余的教室为 ai 个。作为大学借教室服 务的负责人,你需要完成如下三种操作共 m 次:
1 第l天到第r天,每天被归还d个教室。 2 询问第 l 天到第 r 天教室个数的平均数。 3 询问第 l 天到第 r 天教室个数的方差。
「输入格式」
第一行包括两个正整数 n 和 m,其中 n 为借教室的天数,m 为操作次数。 接下来一行,共包含 n 个整数,第 i 个整数表示第 i 天剩余教室数目为 ai 个。 接下来 m 行,每行的第一个整数为操作编号(只能为 1 或 2 或 3),接下来
包含两个正整数 l 和 r,若操作编号为 1,则接下来再包含一个正整数 d。 「输出格式」
对于每个操作 2 和操作 3,输出一个既约分数(分子与分母互质)表示询问 的答案(详见样例)。若答案为 0,请输出“0/1”(不含引号)。
「样例输入」
54
1 2345 1 123 2 24
3 24
3 15
「样例输出」
4/1 2/3 14/25
「样例说明」
初始情况下,剩余教室数量为(1, 2, 3, 4, 5)。 第1次操作为第1天到第2天归还3个教室,变为(4, 5, 3, 4, 5)。
第2次操作询问第2天到第4天的平均数为5+3+4 = 4。 31
第3次操作询问第2天到第4天的方差为1+1+0 = 2。 33
第4次操作询问第1天到第5天的方差为 0.04 + 0.64 +1.44 + 0.04 + 0.64 = 14 。 5 25
「数学小贴士」
1n 1n n个数的平均数为x= ∑x,n个数的方差为 ∑(x−x)2。
「数据规模与约定」
n,m<=100000
只要用线段树维护区间和以及区间平方和T T
然后推推公式神马的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstdlib> #define inf 1000000000 #define ll long long using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,m; struct seg{int l,r;ll size,s1,s2,tag;}t[400005]; ll gcd(ll x,ll y) { return y==0?x:gcd(y,x%y); } void pushdown(int k) { if(t[k].l==t[k].r||!t[k].tag)return; ll tag=t[k].tag;t[k].tag=0; t[k<<1].tag+=tag;t[k<<1|1].tag+=tag; t[k<<1].s2+=2*tag*t[k<<1].s1+tag*tag*t[k<<1].size; t[k<<1|1].s2+=2*tag*t[k<<1|1].s1+tag*tag*t[k<<1|1].size; t[k<<1].s1+=t[k<<1].size*tag; t[k<<1|1].s1+=t[k<<1|1].size*tag; } seg merge(seg a,seg b) { seg tmp; tmp.tag=0; tmp.l=a.l;tmp.r=b.r;tmp.size=a.size+b.size; tmp.s1=a.s1+b.s1;tmp.s2=a.s2+b.s2; return tmp; } void build(int k,int l,int r) { t[k].l=l;t[k].r=r; if(l==r) { t[k].s1=read();t[k].s2=t[k].s1*t[k].s1;t[k].size=1;return; } int mid=(l+r)>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); t[k]=merge(t[k<<1],t[k<<1|1]); } seg query(int k,int x,int y) { pushdown(k); int l=t[k].l,r=t[k].r; if(l==x&&y==r)return t[k]; int mid=(l+r)>>1; if(y<=mid)return query(k<<1,x,y); else if(x>mid)return query(k<<1|1,x,y); else { return merge(query(k<<1,x,mid),query(k<<1|1,mid+1,y)); } } void add(int k,int x,int y,ll val) { pushdown(k); int l=t[k].l,r=t[k].r; if(l==x&&y==r) { t[k].s2+=2*val*t[k].s1+val*val*t[k].size; t[k].s1+=t[k].size*val; t[k].tag+=val; return; } int mid=(l+r)>>1; if(y<=mid)add(k<<1,x,y,val); else if(x>mid)add(k<<1|1,x,y,val); else { add(k<<1,x,mid,val); add(k<<1|1,mid+1,y,val); } t[k]=merge(t[k<<1],t[k<<1|1]); } void print(ll x,ll y) { ll t=gcd(x,y); printf("%lld/%lld\n",x/t,y/t); } int main() { n=read();m=read(); build(1,1,n); for(int i=1;i<=m;i++) { int f=read(),x=read(),y=read(),d; if(f==1)d=read(),add(1,x,y,d); else if(f==2) { seg t=query(1,x,y); print(t.s1,t.size); } else { seg t=query(1,x,y); print(t.s2*t.size-t.s1*t.s1,t.size*t.size); } } return 0; } |
关键就是方差那个公式化简的另一种形式不知道TAT……