【poj 3468】A Simple Problemwith Integersベースラインツリーorツリー配列
以前のブログでは線分樹をあまり書いたことがないと言いました.今から補充します.
ACコードは以下の通りです(最初は負数読込最適化スラグWAを何発か見ていませんでした...本当に無言です):
by lych
2016.1.8
-----------------------------------------------------------------------------------------------
後で木の配列の書き方があることに気づいて、1発補った(後で木の配列を書いたに違いない1 kコード).
区間修正単一点クエリはツリー配列で完了できることを知っています.元の配列がaであると仮定し,補助配列,例えばbを用いてa[i]をb[1]+b[2]+...+に変換する.b[i],すなわちa配列がb配列の接頭辞和に相当し,単点を接頭辞和に変換すると,区間修正(x,y)にzが加算され,b[x]+=z,b[y+1]-=zであれば,単純な推理ではa[i]=b[1]+b[2]+...b[i].そこで、メンテナンス接頭辞の和を単一の変更に変換します.区間クエリを行う場合は、接頭辞和の接頭辞和を維持することに相当します.これはbzojにこのような問題があります.具体的には忘れています.
実際、c[i]=a[1]+a[2]+...a[i]=b[1]*i+b[2]*(i-1)+...+b[i]*1=b[1]*n+b[2]*(n-1)+...+b[i]*(n-i+1)-(b[1]+b[2]+...+b[n]*(n-i)つまり、b配列の接頭辞と、b[1]*n,b[2]*(n-1)のような形を維持し、b[n]*1の配列の接頭辞和は二重接頭辞和を得ることができる.ツリー配列を利用すればよい.
MACコードは以下の通りである.
by lych
2016.1.9
ACコードは以下の通りです(最初は負数読込最適化スラグWAを何発か見ていませんでした...本当に無言です):
<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cstring>
#define N 500005
#define ll long long
int n,m,icr[N],c[N][2]; ll sum[N];
int read(){
int x=0,tmp=1; char ch=getchar();
while (ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if (ch=='-'){ tmp=-1; ch=getchar(); }
while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
return x*tmp;
}
void pushdown(int k){
if (icr[k]){
int l=c[k][0],r=c[k][1],mid=(l+r)>>1;
icr[k<<1]+=icr[k]; icr[k<<1|1]+=icr[k];
sum[k<<1]+=(ll)icr[k]*(mid-l+1); sum[k<<1|1]+=(ll)icr[k]*(r-mid);
icr[k]=0;
}
}
void build(int k,int x,int y){
c[k][0]=x; c[k][1]=y; icr[k]=0;
if (x==y){ sum[k]=read(); return; } else{
int mid=(x+y)>>1;
build(k<<1,x,mid); build(k<<1|1,mid+1,y);
sum[k]=sum[k<<1]+sum[k<<1|1];
}
}
void ins(int k,int x,int y,int v){
int l=c[k][0],r=c[k][1],mid=(l+r)>>1;
if (x==l && y==r){
icr[k]+=v; sum[k]+=(ll)v*(r-l+1); return;
}
pushdown(k);
if (y<=mid) ins(k<<1,x,y,v); else
if (x>mid) ins(k<<1|1,x,y,v); else{
ins(k<<1,x,mid,v); ins(k<<1|1,mid+1,y,v);
}
sum[k]=sum[k<<1]+sum[k<<1|1];
}
ll qry(int k,int x,int y){
int l=c[k][0],r=c[k][1],mid=(l+r)>>1;
if (x==l && y==r) return sum[k];
pushdown(k);
if (y<=mid) return qry(k<<1,x,y); else
if (x>mid) return qry(k<<1|1,x,y); else{
ll tmp=qry(k<<1,x,mid); tmp+=qry(k<<1|1,mid+1,y);
return tmp;
}
}
int main(){
while (~scanf("%d%d",&n,&m)){
build(1,1,n); int i; char ch;
for (i=1; i<=m; i++){
ch=getchar(); while (ch!='Q' && ch!='C') ch=getchar();
if (ch=='C'){
int x=read(),y=read(),v=read(); ins(1,x,y,v);
} else{ int x=read(),y=read(); printf("%I64d
",qry(1,x,y)); }
}
}
return 0;
}</span>
by lych
2016.1.8
-----------------------------------------------------------------------------------------------
後で木の配列の書き方があることに気づいて、1発補った(後で木の配列を書いたに違いない1 kコード).
区間修正単一点クエリはツリー配列で完了できることを知っています.元の配列がaであると仮定し,補助配列,例えばbを用いてa[i]をb[1]+b[2]+...+に変換する.b[i],すなわちa配列がb配列の接頭辞和に相当し,単点を接頭辞和に変換すると,区間修正(x,y)にzが加算され,b[x]+=z,b[y+1]-=zであれば,単純な推理ではa[i]=b[1]+b[2]+...b[i].そこで、メンテナンス接頭辞の和を単一の変更に変換します.区間クエリを行う場合は、接頭辞和の接頭辞和を維持することに相当します.これはbzojにこのような問題があります.具体的には忘れています.
実際、c[i]=a[1]+a[2]+...a[i]=b[1]*i+b[2]*(i-1)+...+b[i]*1=b[1]*n+b[2]*(n-1)+...+b[i]*(n-i+1)-(b[1]+b[2]+...+b[n]*(n-i)つまり、b配列の接頭辞と、b[1]*n,b[2]*(n-1)のような形を維持し、b[n]*1の配列の接頭辞和は二重接頭辞和を得ることができる.ツリー配列を利用すればよい.
MACコードは以下の通りである.
<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 100005
using namespace std;
int n,m; ll c[2][N];
int read(){
int x=0,tmp=1; char ch=getchar();
while (ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if (ch=='-'){ tmp=-1; ch=getchar(); }
while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
return x*tmp;
}
void ins(int k,int x,ll t){
int i; for (i=x; i<=n; i+=i&(-i)) c[k][i]+=t;
}
ll qry(int k,int x){
ll sum=0,i; for (i=x; i; i-=i&(-i)) sum+=c[k][i]; return sum;
}
void mdy(int x,int y,ll t){
ins(0,x,t); ins(0,y+1,-t);
ins(1,x,t*(n-x+1)); ins(1,y+1,-t*(n-y));
}
int main(){
while (~scanf("%d%d",&n,&m)){
int i; char ch; memset(c,0,sizeof(c));
for (i=1; i<=n; i++){
int tmp=read(); mdy(i,i,(ll)tmp);
}
while (m--){
ch=getchar(); while (ch!='Q' && ch!='C') ch=getchar();
if (ch=='C'){
int x=read(),y=read(),z=read(); mdy(x,y,(ll)z);
} else{
int x=read()-1,y=read();
printf("%I64d
",qry(1,y)-qry(0,y)*(n-y)-qry(1,x)+qry(0,x)*(n-x));
}
}
}
return 0;
}
</span>
by lych
2016.1.9