2018 icpc徐州站網路賽 H ——Ryuji doesn't want to study ——(線段樹)
阿新 • • 發佈:2018-12-15
題目思路:很明顯的一個線段樹題目,但是要求的是a[l]*len+a[l+1]*(len-1)+a[l+2]*(len-2)+......+a[r],所以根據這個推得以下公式:
可以試著推一下這個公式。所以我們維護兩個線段樹,sum1儲存正常的陣列區間和,sum2儲存a[i]*i的陣列的區間和,
當查詢區間為[l,r]時,答案就是sum2[l,r]-(n-r)*sum1[l,r]
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; /* 維護兩個線段樹sum1和sum2,sum1維護正常區間和,sum2維護(n-i+1)*a[i]的區間和 答案就是smu2[l,r]-(n-r)sum1[l,r] 公式可以自己推一下 */ const int maxn=1e5+10; typedef long long ll; ll sum1[maxn*4],sum2[maxn*4],a[maxn];//線段樹開4倍空間 int n; void build(int o,int l,int r) { if(l==r) { sum1[o]=a[l]; sum2[o]=a[l]*(n-l+1); return; } int ls=o*2,rs=o*2+1,m=(l+r)/2;//左右子節點和中間節點 build(ls,l,m); build(rs,m+1,r); sum1[o]=sum1[ls]+sum1[rs]; sum2[o]=sum2[ls]+sum2[rs]; } //查詢sum1 ll query1(int o,int l,int r,int ql,int qr) { if(l>=ql&&r<=qr)return sum1[o]; int ls=2*o,rs=2*o+1,m=(l+r)/2; ll res=0; if(ql<=m)res+=query1(ls,l,m,ql,qr); if(qr>m)res+=query1(rs,m+1,r,ql,qr); return res; } //查詢sum2 ll query2(int o,int l,int r,int ql,int qr) { if(l>=ql&&r<=qr)return sum2[o]; int ls=2*o,rs=2*o+1,m=(l+r)/2; ll res=0; if(ql<=m)res+=query2(ls,l,m,ql,qr); if(qr>m)res+=query2(rs,m+1,r,ql,qr); return res; } //更新 void update(int o,int l,int r,int k,ll v) { if(l==r) { sum1[o]=v; sum2[o]=v*(n-l+1); return; } int ls=o*2,rs=o*2+1,m=(l+r)/2; if(k<=m)update(ls,l,m,k,v); else update(rs,m+1,r,k,v); sum1[o]=sum1[ls]+sum1[rs]; sum2[o]=sum2[ls]+sum2[rs]; } int main() { int q; while(scanf("%d%d",&n,&q)!=EOF) { for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); } build(1,1,n); while(q--) { int x; scanf("%d",&x); if(x==1) { int l,r; scanf("%d%d",&l,&r); ll temp1=query1(1,1,n,l,r); ll temp2=query2(1,1,n,l,r); ll temp3=n-r; printf("%lld\n",temp2-temp3*temp1); } else { int k; ll v; scanf("%d%lld",&k,&v); update(1,1,n,k,v); } } } return 0; }