【 题 意 】 : \color{orange}{【题意】:} 【题意】: 蒟蒻HansBug
在一本数学书里面发现了一个神奇的数列,包含 N N N个实数。他想算算这个数列的平均数和方差。数据带修改。
【注】:方差:方差是计算一个数列数据波动情况的重要量,记为 s 2 s^2 s2。记数据为 A A A, P P P为平均数,则方差计算公式为:
s 2 = ∑ i = 1 n ( A i − P ) 2 n s^2=\frac{\sum\limits_{i=1}^{n} (A_i-P)^2}{n} s2=ni=1∑n(Ai−P)2
【 思 路 】 : \color{orange}{【思路】:} 【思路】: 我们把方差的公式稍微修改一下,得:
所以,现在我们只需维护区间的平方和与区间和即可。
而对于区间的平方和,如果我们令区间同时加上一个数 x x x,我们有:
(以上图片依旧选自一篇洛谷题解)
然后,我们就可以直接用线段树维护求解。
【 代 码 】 : \color{orange}{【代码】:} 【代码】:
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
const int N=1e5+100;
double sum[N<<2],add[N<<2];
double seg[N<<2],a[N];
inline void pushup(int o){
sum[o]=sum[o<<1]+sum[o<<1|1];
seg[o]=seg[o<<1]+seg[o<<1|1];
}
inline void pushdown(int o,int l,int r){
double tag=add[o];add[o]=0;
add[o<<1|1]+=tag;add[o<<1]+=tag;
register int mid=(l+r)>>1;//注意顺序,一定要先修改seg,再修改sum
seg[o<<1]+=2*tag*sum[o<<1]+(mid-l+1)*tag*tag;
seg[o<<1|1]+=2*tag*sum[o<<1|1]+(r-mid)*tag*tag;
sum[o<<1]+=tag*(mid-l+1);sum[o<<1|1]+=tag*(r-mid);
}
void build(int o,int l,int r){
add[o]=0.0;
if (l==r){
sum[o]=a[l];
seg[o]=a[r]*a[r];
return;
}
register int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);return;
}
void updata(int o,int l,int r,int p,int q,double v){
if (l>q||r<p) return;
if (p<=l&&r<=q){
seg[o]+=2*v*sum[o]+(r-l+1)*v*v;
sum[o]+=v*(r-l+1);add[o]+=v;return;
}
if (add[o]) pushdown(o,l,r);
register int mid=(l+r)>>1;
updata(o<<1,l,mid,p,q,v);
updata(o<<1|1,mid+1,r,p,q,v);
pushup(o);return;
}
double query_sum(int o,int l,int r,int p,int q){
if (l>q||r<p) return 0.0;
if (p<=l&&r<=q) return sum[o];
if (add[o]) pushdown(o,l,r);
register int mid=(l+r)>>1;
register double answer=0.0;
answer+=query_sum(o<<1,l,mid,p,q);
answer+=query_sum(o<<1|1,mid+1,r,p,q);
return answer;
}
double query_squ(int o,int l,int r,int p,int q){
if (l>q||r<p) return 0.0;
if (p<=l&&r<=q) return seg[o];
if (add[o]) pushdown(o,l,r);
register int mid=(l+r)>>1;
register double answer=0.0;
answer+=query_squ(o<<1,l,mid,p,q);
answer+=query_squ(o<<1|1,mid+1,r,p,q);
return answer;
}
int n,m,opt,l,r;
int main(){
freopen("t1.in","r",stdin);
n=read();m=read();
for(int i=1;i<=n;i++)
scanf("%lf",&a[i]);
build(1,1,n);
for(int i=1;i<=m;i++){
opt=read();l=read();r=read();
if (opt==1){
register double v;
scanf("%lf",&v);
updata(1,1,n,l,r,v);
}
else{
double ave=query_sum(1,1,n,l,r)/(r-l+1.0);
if (opt==2) printf("%.4lf\n",ave);
else printf("%.4lf\n",-ave*ave+query_squ(1,1,n,l,r)/(r-l+1));
}
}
return 0;
}
【 题 意 】 : \color{orange}{【题意】:} 【题意】: 阿宝
上学了,今天老师拿来了一块很长的涂色板。
色板长度为 L L L, L L L是一个正整数,所以我们可以均匀地将它划分成 L L L块 1 1 1厘米长的小方格。并从左到右标记为 1 , 2 , . . . L 1, 2, ... L 1,2,...L。
现在色板上只有一个颜色,老师告诉阿宝在色板上只能做两件事:
C A B C
指在 A A A到 B B B 号方格中涂上颜色 C C C。P A B
指老师的提问: A A A到 B B B号方格中有几种颜色。学校的颜料盒中一共有 T T T 种颜料。为简便起见,我们把他们标记为 1 , 2 , . . . T 1, 2, ... T 1,2,...T。 开始时色板上原有的颜色就为 1 1 1号色。 面对如此复杂的问题,阿宝
向你求助,你能帮助他吗?
【 思 路 】 : \color{orange}{【思路】:} 【思路】: 乍一看这题很难,但是 1 ≤ T ≤ 30 1 \leq T \leq 30 1≤T≤30,所以我们可以把区间每个位置的颜色用二进制保存,然后就可以用线段树维护了。
【 代 码 】 : \color{orange}{【代码】:} 【代码】:
const int N=1e5+100;
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
int sum[N<<2],add[N<<2];
inline void pushup(int o){
sum[o]=sum[o<<1]|sum[o<<1|1];
}
inline void pushdown(int o){
int tag=add[o];add[o]=0;
add[o<<1]=tag;add[o<<1|1]=tag;
sum[o<<1]=tag;sum[o<<1|1]=tag;
}
void build(int o,int l,int r){
if (l==r){
sum[o]=1;return;
}
register int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);return;
}
void updata(int o,int l,int r,int p,int q,int v){
if (l>q||r<p) return;
if (p<=l&&r<=q){
add[o]=1<<(v-1);
sum[o]=1<<(v-1);
return;
}
if (add[o]) pushdown(o);
register int mid=(l+r)>>1;
updata(o<<1,l,mid,p,q,v);
updata(o<<1|1,mid+1,r,p,q,v);
pushup(o);return;
}
int query(int o,int l,int r,int p,int q){
if (l>q||r<p) return 0;
if (p<=l&&r<=q) return sum[o];
if (add[o]) pushdown(o);
register int mid=(l+r)>>1;
register int answer=0;
answer|=query(o<<1,l,mid,p,q);
answer|=query(o<<1|1,mid+1,r,p,q);
return answer;
}
int n,m,color;//n:L;m:O;color:T
int calc(int x){//计算x的二进制中有多少个1
register int cnt=0;
for(int i=0;i<color;i++)
if (x&(1<<i))
cnt++;
return cnt;
}
int main(){
n=read();color=read();
m=read();build(1,1,n);
for(int i=1;i<=m;i++){
register char opt;cin>>opt;
register int l=read(),r=read();
if (l>r) swap(l,r);//Don't forget it!!!
if (opt=='C'){
register int v=read();
updata(1,1,n,l,r,v);
}
else printf("%d\n",calc(query(1,1,n,l,r)));
}
return 0;
}
【 题 意 】 : \color{orange}{【题意】:} 【题意】: 面对蚂蚁们的疯狂进攻,小FF
的Tower defence
宣告失败……人类被蚂蚁们逼到了Greed Island
上的一个海湾。现在,小FF
的后方是一望无际的大海, 前方是变异了的超级蚂蚁。小FF
还有大好前程,他可不想命丧于此, 于是他派遣手下最后一批改造SCV
布置地雷以阻挡蚂蚁们的进攻。
小FF
最后一道防线是一条长度为 N N N的战壕,小FF
拥有无数多种地雷,而SCV
每次可以在 [ L , R ] [ L , R ] [L,R]区间埋放同一种不同于之前已经埋放的地雷。 由于情况已经十万火急,小FF在某些时候可能会询问你在 [ L ′ , R ′ ] [ L' , R'] [L′,R′] 区间内有多少种不同的地雷, 他希望你能尽快的给予答复。
【 思 路 】 : \color{orange}{【思路】:} 【思路】: 每次埋地雷,我们可以看作以下两个事件:
(
,表示一种新地雷的开始)
,表示一种地雷的结束位置对于查询操作,我们先查询 R R R以前有多少中地雷,即查询 R R R以前有多少个(
,当然有些地雷可能不在 [ L , R ] [L,R] [L,R]内,它们有一个共性:即结束位置 < L (
的数量减去 L L L以前的)
的数量。
于是,我们分别开两个树状数组维护(
和)
即可。总的时间复杂度: O ( M × l o g N ) O(M \times log N) O(M×logN)。
【 代 码 】 : \color{orange}{【代码】:} 【代码】:
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
const int N=1e5+1e3;
typedef long long ll;
ll c[N][3];int n,m,opt,l,r;
inline int F(int x){
return x&(-x);
}
inline void updata(int x,int p){
for(;x<=n;x+=F(x)) c[x][p]++;
}
inline ll query(int x,int p){
register ll ans=0ll;
for(;x;x-=F(x))
ans+=c[x][p];
return ans;
}
int main(){
n=read();m=read();
for(int i=1;i<=m;i++){
opt=read();l=read();r=read();
if (opt==1){
updata(l,1);
updata(r,2);
}
else printf("%lld\n",query(r,1)-query(l-1,2));
}
return 0;
}