感觉文章中出现问题或者看不懂的部分请评论提醒。
更新档案:
time | events |
---|---|
16/9/27 | 更新第一版 |
16/10/21 | 更新《基础排序算法》《基础图论算法》 |
16/10/22 | NOIP2016初赛 |
16/10/31 | 更新模板“高精度” |
16/11/14 | 更新模板“线段树” |
假设题目名为“add”,那么文件夹名为“add”,c++程序名为“add.cpp”,读入文件名为“add.in”,输出文件名为“add.out”。
Focus:以上四个的拼写均不可有误,包括大小写差异。
#include
int main(){
freopen("add.in","r",stdin);//read
freopen("add.out","w",stdout);//write
}
千万不要调试后就忘记修改文件读入读出了。
一般性的对拍可以借助命令行比较文件,需要两个输出的内容都在同一个文件夹下。
windows键+R,输入cmd,打开命令行,再根据下面内容依次输入:cd+当前文件夹地址->fc(比较)两个文件。
分别是相同与不同的情况。
比较高级的对拍需要造数据程序(data.exe),保证正确性的暴力对拍程序(test.exe)与测试程序(以moo.exe为例)。下面是对拍的代码,写在txt中再转成.bat即可。
:loop
data.exe
test.exe
moo.exe
fc moo.out test.out
if %errorlevel% ==0 goto loop
pause
首先是比较实用的所有 O(nlogn) 算法。
快速排序的核心思想:
快速排序的复杂度:
快速排序的实现:
考虑用 O(n) 的复杂度实现按基底大小分配到左右区间的操作。这里推荐两种算法:
#include
inline void Rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
}
int res[100005];
void qsort(int L,int R){
if(L>=R)return;
int key=res[L],low=L,high=R;
while(lowwhile(lowif(low//当前res[high]满足key值左侧,故扔给low位置
while(low=res[low])++low;
if(low1),qsort(low+1,R);
}
int main(){
int n;Rd(n);
for(int i=1;i<=n;i++)Rd(res[i]);
qsort(1,n);
for(int i=1;i<=n;i++)
printf("%d%c",res[i],i==n?'\n':' ');
}
#include
inline void Rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
}
const int M=100005;
int res[M];
void swap(int *a,int *b){
if(a==b)return;
int t=*a;*a=*b;*b=t;
}
void qsort(int L,int R){
if(L>=R)return;
int key=res[R],low=L;
for(int high=L;highif(res[high]<=key)swap(&res[low++],&res[high]);
swap(&res[low],&res[R]);
qsort(L,low-1),qsort(low+1,R);
}
int main(){
int n;Rd(n);
for(int i=1;i<=n;i++)Rd(res[i]);
qsort(1,n);
for(int i=1;i<=n;i++)
printf("%d%c",res[i],i==n?'\n':' ');
}//该算法转自Codevs
快速排序的拓展运用:求序列第k大数,平均复杂度 O(n) 。
#include
#include
#include
#include
#define M 10000005
using namespace std;
int A[M];
void swap(int &a,int &b){int t=a;a=b;b=t;}
int qsort(int L,int R,int k){
if(L==R)return L;
int tot=1LL*rand()*rand()%(R-L)+L;
swap(A[L],A[tot]);
int val=A[L];
int low=L,high=R;
while(lowwhile(low=A[high])--high;
if(lowwhile(low=val)++low;
if(lowif(lowreturn qsort(low+1,R,k);
if(low>k)return qsort(L,low-1,k);
return low;
}
int main(){
srand(time(NULL));
int n,k;
scanf("%d %d %d",&n,&A[1],&k);
for(int i=2;i<=n;i++)
A[i]=1LL*A[i-1]*A[i-1]%P;
printf("%d\n",A[qsort(1,n,k)]);
return 0;
}
归并排序的核心思想:
归并排序的复杂度:
归并排序的实现:
#include
inline void Rd(int &res){
res=0;char c;short f=1;
while(c=getchar(),c<48&&c!='-');
do if(c=='-')f=-1;
else res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
res*=f;
}
const int M=1000005;
int a[M],b[M];
void Merge(int L,int R){
if(L==R)return;
int mid=L+R>>1;
Merge(L,mid);Merge(mid+1,R);
int low=L,high=mid+1,c=L;
while(low<=mid&&high<=R)//[L,low)
if(a[low]<=a[high])b[c++]=a[low++];
else b[c++]=a[high++];
while(low<=mid)b[c++]=a[low++];
while(high<=R)b[c++]=a[high++];
for(int i=L;i<=R;i++)a[i]=b[i];
}
int main(){
int n;Rd(n);
for(int i=1;i<=n;i++)Rd(a[i]);
Merge(1,n);
for(int i=1;i<=n;i++)
printf("%d%c",a[i],i==n?'\n':' ');
}
归并排序的拓展运用:求逆序对个数。
#include
inline void Rd(int &res){
res=0;char c;short f=1;
while(c=getchar(),c<48&&c!='-');
do if(c=='-')f=-1;
else res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
res*=f;
}
const int M=1000005;
int a[M],b[M];long long cnt=0;
void Merge(int L,int R){
if(L==R)return;
int mid=L+R>>1;
Merge(L,mid);Merge(mid+1,R);
int low=L,high=mid+1,c=L;
while(low<=mid&&high<=R)//[L,low)
if(a[low]<=a[high])b[c++]=a[low++];
else{
cnt+=mid-(low-1);
b[c++]=a[high++];
}
while(low<=mid)b[c++]=a[low++];
while(high<=R)b[c++]=a[high++];
for(int i=L;i<=R;i++)a[i]=b[i];
}
int main(){
int n;Rd(n);
for(int i=1;i<=n;i++)Rd(a[i]);
Merge(1,n);
printf("%lld\n",cnt);
}
堆排序的核心思想:
堆排序的复杂度:
堆排序的实现:
#include
inline void Rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
}
//Heap_sort
//对于一个节点node,父亲结点node/2,左右儿子节点node*2(+1)
struct Heap{
static const int M=100005;
int heap[M],sz;
Heap(){sz=0;}
inline void swap(int *a,int *b){
if(a==b)return;
int t=*a;*a=*b;*b=t;
}
int top(){return heap[1];}
void push(int val){
heap[++sz]=val;
int pos=sz;
while(pos>>1){
int nxt=pos>>1;
if(heap[nxt]>heap[pos])swap(&heap[nxt],&heap[pos]);
else break;
pos=nxt;
}
}
void pop(){
int pos=1;
heap[pos]=heap[sz--];
while((pos<<1)<=sz){
int nxt=pos<<1;
if(nxt+1<=sz&&heap[nxt+1]//小顶堆
if(heap[nxt]pos])swap(&heap[pos],&heap[nxt]);
else break;
pos=nxt;
}
}
}q;
int main(){
int n;Rd(n);
for(int i=1,x;i<=n;++i)Rd(x),q.push(x);
for(int i=1;i<=n;i++){
printf("%d",q.top());
putchar(i==n?'\n':' ');
q.pop();
}
}
四个基本排序分别为计数排序、选择排序、插入排序、冒泡排序,除计数排序的复杂度为 O(num) ,其余排序的复杂度均为 O(n2) 。
基数排序的核心思想:
基数排序的复杂度:
基数排序的实现:
#include
inline void Rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
}
static const int M=100005,S=10;
int a[M],s[S][M],sz[S];
int main(){
int n;Rd(n);
for(int i=1;i<=n;++i)Rd(a[i]);
for(int base=1,i=1;ibase*=10){
for(int j=0;j0;
for(int j=1;j<=n;j++){
int step=a[j]/base%10;
s[step][++sz[step]]=a[j];
}
int tot=0;
for(int j=0;jfor(int k=1;k<=sz[j];k++)
a[++tot]=s[j][k];
}
for(int i=1;i<=n;i++)
printf("%d%c",a[i],i==n?'\n':' ');
}
Dijkstra的实现:
#include
#include
inline void Rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
}
int min(int a,int b){return astatic const int M=1005;
int n,m,G[M][M],dis[M];
bool used[M];
void Dijkstra(int st){//暴力O(n^2)的实现
memset(dis,-1,sizeof(dis));
memset(used,0,sizeof(used));
dis[st]=0;
int u=-1;
while(true){
u=-1;
for(int i=1;i<=n;i++)
if(~dis[i]&&!used[i]&&(u==-1||dis[i]if(u==-1)break;
used[u]=true;
for(int v=1;v<=n;v++)
if(~G[u][v]&&!used[v]&&(dis[v]==-1||dis[v]>dis[u]+G[u][v]))
dis[v]=dis[u]+G[u][v];
}
}
int main(){
Rd(n),Rd(m);
memset(G,-1,sizeof(G));
for(int i=1,u,v,w;i<=m;i++){
Rd(u),Rd(v),Rd(w);
G[u][v]=~G[u][v]?min(G[u][v],w):w;
}
Dijkstra(1);
printf("%d\n",dis[n]);
}
#include
using namespace std;
inline void Rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
}
static const int M=100005;
struct edge{int v,w,nxt;}Edges[M];
int head[M];
void add_edge(int u,int v,int w,int &top){
Edges[++top]=(edge){v,w,head[u]};head[u]=top;//链表实现邻接表
}
int dis[M];
bool used[M];
struct node{
int u,dis;
bool operator < (const node &cmp)
const {return dis>cmp.dis;}//优先队列中'<'表示大顶堆
};
int Dijkstra(int st,int gt){//O(nlogn)堆优化
priority_queueq;
memset(dis,-1,sizeof(dis));
memset(used,0,sizeof(used));
q.push((node){st,0});dis[st]=0;
while(!q.empty()){
node now=q.top();q.pop();
if(now.u==gt)return now.dis;
if(used[now.u])continue;
used[now.u]=true;
for(int j=head[now.u];~j;j=Edges[j].nxt){
edge nxt=Edges[j];
if(used[nxt.v])continue;
if(dis[nxt.v]==-1||dis[nxt.v]>nxt.w+dis[now.u]){
dis[nxt.v]=nxt.w+dis[now.u];
q.push((node){nxt.v,dis[nxt.v]});
}
}
}
return -1;
}
int main(){
int n,m,st,gt,top=0;
Rd(n),Rd(m),Rd(st),Rd(gt);
memset(head,-1,sizeof(head));
for(int i=1,u,v,w;i<=m;i++){
Rd(u),Rd(v),Rd(w);
add_edge(u,v,w,top);
}
printf("%d\n",Dijkstra(st,gt));
}
Bellman_Ford的实现:
#include
using namespace std;
inline void Rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
}
static const int M=1005,inf=0x3f3f3f3f;
struct edge{int u,v,w;}G[M*M];
int n,m,dis[M];
void Bellman_Ford(int st){//O(n*m),紧上界
memset(dis,0x3f,sizeof(dis));
dis[st]=0;
for(int t=1;tfor(int j=1;j<=m;j++)
if(dis[G[j].v]>1ll*dis[G[j].u]+G[j].w)
dis[G[j].v]=dis[G[j].u]+G[j].w;
/*
进行第n次操作,即判负环操作。
bool flag=true;
for(int j=1;j<=m;j++)
if(dis[G[j].v]>1ll*dis[G[j].u]+G[j].w)
{flag=false;break;}
return flag;
*/
}
int main(){
Rd(n),Rd(m);
for(int i=1,u,v,w;i<=m;i++)
Rd(G[i].u),Rd(G[i].v),Rd(G[i].w);
Bellman_Ford(1);
if(dis[n]==inf)puts("-1");
else printf("%d\n",dis[n]);
}
SPFA算法优化在将Bellman_Ford的松弛点减少,即只将所有成功松弛过的点加入下一轮去松弛其他点的队列中,同时已经在松弛队列内的点不重复加入,就得到该算法了。同样的,如果有一点内加入松弛队列的次数大于n-1次,说明出现了负环。
SPFA的实现:
#include
using namespace std;
inline void Rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
}
static const int M=100005,inf=0x3f3f3f3f;
struct edge{int to,val,nxt;}Edges[M];
int head[M],etop=0;
void add_edge(int u,int v,int w){
Edges[etop]=(edge){v,w,head[u]};
head[u]=etop++;
}//邻接表
int n,m,st,gt,dis[M];
bool used[M];
void SPFA(int st){//O(n*m),松上界
memset(dis,0x3f,sizeof(dis));
memset(used,0,sizeof(used));
queue<int>q;
q.push(st);dis[st]=0;
while(!q.empty()){
int u=q.front();q.pop();
used[u]=false;
for(int j=head[u];~j;j=Edges[j].nxt){
int v=Edges[j].to,w=Edges[j].val;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(used[v])continue;
used[v]=true;
q.push(v);
}
}
}
}
int main(){
Rd(n),Rd(m),Rd(st),Rd(gt);
memset(head,-1,sizeof(head));
for(int i=1,u,v,w;i<=m;i++){
Rd(u),Rd(v),Rd(w);
add_edge(u,v,w);
}
SPFA(st);
if(dis[gt]>=inf)dis[gt]=-1;
printf("%d\n",dis[gt]);
return 0;
}
Floyd的实现:
#include
#include
static const int M=1005,inf=0x3f3f3f3f;
int n,m,dis[M][M];
void Floyd(){
for(int k=1;k<=n;k++)//中间节点
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[i][j]=dis[i][k]+dis[k][j];
}
int main(){
scanf("%d %d",&n,&m);
memset(dis,0x3f,sizeof(dis));
for(int i=1,u,v,w;i<=m;i++){
scanf("%d %d %d",&u,&v,&w);
if(dis[u][v]>w)dis[u][v]=w;
}
Floyd();
if(dis[1][n]==inf)puts("-1");
else printf("%d\n",dis[1][n]);
return 0;
}
Kruskal的实现:
#include
using namespace std;
inline void Rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
}
static const int M=100005;
struct edge{
int u,v,w;
bool operator < (const edge &cmp)
const {return wint n,m,fa[M];
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
int Kruskal(){
int sum=0;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++){
int u=Edges[i].u,v=Edges[i].v;
u=getfa(u),v=getfa(v);
if(u!=v){
sum+=Edges[i].w;
fa[v]=u;
}
}
return sum;
}
int main(){
Rd(n),Rd(m);
for(int i=1,u,v,w;i<=m;i++){
Rd(u),Rd(v),Rd(w);
Edges[i]=(edge){u,v,w};
}
sort(Edges+1,Edges+m+1);
printf("%d\n",Kruskal());
}
Prim算法的 O(n2) 实现:
#include
#include
inline void Rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
}
static const int M=105,inf=0x3f3f3f3f;
int n,G[M][M],dis[M];
bool used[M];
int Prim(){
memset(dis,0x3f,sizeof(dis));
memset(used,0,sizeof(used));
dis[1]=0;
int u=-1,sum=0,val=inf;
while(true){
u=-1,val=inf;
for(int i=1;i<=n;i++)
if(!used[i]&&val>dis[i])u=i,val=dis[i];
if(u==-1)break;
used[u]=true,sum+=val;
for(int v=1;v<=n;v++)
if(!used[v]&&dis[v]>G[u][v])dis[v]=G[u][v];
}
return sum;
}
int main(){
Rd(n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)Rd(G[i][j]);
printf("%d\n",Prim());
}
通常根据不同的题目,高精要支持下列部分操作:
当然通常来说,我们不需要支持负数高精。下面代码给出的是封装在struct结构体内的BigInteger,实际操作时不一定要写成面向对象的形式,只需要其中几个操作即可(各位自取自需)。
高精加法测试 | 高精减法测试 | 高精乘法测试 | 高精除法测试 | 低精除法测试
char str[1005];
struct BigInt{
static const int M=1005,P=10000;
#define clear(x,val) memset(x,val,sizeof(x))
int num[M],len;
BigInt(){clear(num,0),len=1;}
void read(){
scanf("%s",str);
len=0;
int sz=strlen(str);
for(int i=sz-1;i>=0;i-=4){
num[len]=0;
for(int j=max(0,i-3);j<=i;j++)
num[len]=(num[len]<<3)+(num[len]<<1)+(str[j]^48);
++len;
}
while(len>1&&!num[len-1])--len;
}
void print(){
printf("%d",num[len-1]);
for(int i=len-2;i>=0;i--)printf("%04d",num[i]);
putchar('\n');
}
bool operator < (const BigInt &cmp)const{
if(len!=cmp.len)return lenlen;
for(int i=len-1;i>=0;i--)
if(num[i]!=cmp.num[i])return num[i]return false;
}
bool operator > (const BigInt &cmp)const{return cmp<*this;}
bool operator <= (const BigInt &cmp)const{return !(cmp<*this);}
bool operator != (const BigInt &cmp)const{return cmp<*this||*thisbool operator == (const BigInt &cmp)const{return !(cmp<*this||*thisconst int &p){
BigInt B;B=*this;
B.num[0]+=p;
int step=0;
while(B.num[step]>=P){
B.num[step+1]++;
B.num[step]-=P;
++step;
}
while(B.num[B.len])++B.len;
return B;
}
BigInt operator + (const BigInt &A)const{
BigInt B;
B.len=max(A.len,len);
for(int i=0;ilen;i++){
B.num[i]+=num[i]+A.num[i];
if(B.num[i]>=P){
B.num[i]-=P;
B.num[i+1]++;
}
}
while(B.num[B.len])++B.len;
return B;
}
BigInt operator - (const int &p){//保证非负
BigInt B;B=*this;
B.num[0]-=p;
int step=0;
while(B.num[step]<0){
B.num[step]+=P;
B.num[step+1]--;
++step;
}
while(B.len>1&&!B.num[B.len-1])--B.len;
return B;
}
BigInt operator - (const BigInt &A)const{
BigInt B;
B.len=max(A.len,len);
for(int i=0;ilen;i++){
B.num[i]+=num[i]-A.num[i];
if(B.num[i]<0){
B.num[i]+=P;
B.num[i+1]--;
}
}
while(B.len>1&&!B.num[B.len-1])--B.len;
return B;
}
BigInt operator * (const int &p){
BigInt B;
B.len=len;
for(int i=0;i<len;i++){
B.num[i]+=num[i]*p;
if(B.num[i]>=P){
B.num[i+1]+=B.num[i]/P;
B.num[i]%=P;
}
}
while(B.num[B.len])++B.len;
return B;
}
BigInt operator * (const BigInt &A)const{
BigInt B;
B.len=A.len+len-1;
for(int i=0;i<len;i++)
for(int j=0;jlen;j++){
B.num[i+j]+=num[i]*A.num[j];
if(B.num[i+j]>=P){
B.num[i+j+1]+=B.num[i+j]/P;
B.num[i+j]%=P;
}
}
while(B.num[B.len])++B.len;
return B;
}
BigInt operator / (const int &p){
BigInt B=*this;
for(int i=B.len-1;i>=0;i--){
if(i)B.num[i-1]+=B.num[i]%p*P;
B.num[i]/=p;
}
while(B.len>1&&!B.num[B.len-1])--B.len;
return B;
}
BigInt operator / (const BigInt &A)const{
BigInt L,R,res;
if(*thisreturn res;
R=*this;
while(L<=R){
BigInt mid=(L+R)/2;
if((mid*A)<=*this){
res=mid;
L=mid+1;
}else R=mid-1;
}
return res;
}
};
我们以该入门例题进行讨论:
维护一个长度为 n(n≤105) 序列,现有以下四种操作:
- 在序列的pos位置增加val的权值 (val≤109) 。
- 在序列的[L,R]区间内都增加val的权值。
- 查询序列的pos位置的累积权值。
- 查询序列的[L,R]区间内的累积权值和。
保证操作交替进行。
1) 【操作1+操作4】单点更新,区间查询。 测试
struct Segment{
static const int M=100005;
ll tree[M<<2];
void up(int p){tree[p]=tree[p<<1]+tree[p<<1|1];}
void build(int L,int R,int p){
if(L==R){Rd(tree[p]);return;}
int mid=L+R>>1;
build(L,mid,p<<1);
build(mid+1,R,p<<1|1);
up(p);
}
void update(int L,int R,int pos,int val,int p){
if(L==R){tree[p]+=val;return;}
int mid=L+R>>1;
if(pos<=mid)update(L,mid,pos,val,p<<1);
else update(mid+1,R,pos,val,p<<1|1);
up(p);
}
ll query(int L,int R,int l,int r,int p){
if(L==l&&R==r)return tree[p];
int mid=L+R>>1;
if(r<=mid)return query(L,mid,l,r,p<<1);
else if(l>mid)return query(mid+1,R,l,r,p<<1|1);
else return query(L,mid,l,mid,p<<1)+query(mid+1,R,mid+1,r,p<<1|1);
}
}Tree;
2) 【操作2+操作3】区间更新+单点查询。 测试
struct Segment{
static const int M=100005;
ll tree[M<<2];
void build(int L,int R,int p){
if(L==R){Rd(tree[p]);return;}
int mid=L+R>>1;
build(L,mid,p<<1);
build(mid+1,R,p<<1|1);
}
void update(int L,int R,int l,int r,int w,int p){
if(L==l&&R==r){tree[p]+=w;return;}
int mid=L+R>>1;
if(r<=mid)update(L,mid,l,r,w,p<<1);
else if(l>mid)update(mid+1,R,l,r,w,p<<1|1);
else{
update(L,mid,l,mid,w,p<<1);
update(mid+1,R,mid+1,r,w,p<<1|1);
}
}
ll query(int L,int R,int pos,int p){
if(L==R)return tree[p];
int mid=L+R>>1;
if(pos<=mid)return tree[p]+query(L,mid,pos,p<<1);
else return tree[p]+query(mid+1,R,pos,p<<1|1);
}
}Tree;
3) 【操作2+操作4】区间更新+区间查询。 测试
(调了好久结果发现是读入挂的锅qvq)
struct Segment{
static const int M=200005;
struct Node{ll sum,add;}tree[M<<2];
void up(int p){
tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum;
}
void down(int L,int R,int p){
if(tree[p].add){
int mid=L+R>>1;
tree[p<<1].add+=tree[p].add;
tree[p<<1|1].add+=tree[p].add;
tree[p<<1].sum+=tree[p].add*(mid-L+1);
tree[p<<1|1].sum+=tree[p].add*(R-mid);
tree[p].add=0;
}
}
void build(int L,int R,int p){
tree[p].add=0;
if(L==R){Rd(tree[p].sum);return;}
int mid=L+R>>1;
build(L,mid,p<<1);
build(mid+1,R,p<<1|1);
up(p);
}
void update(int L,int R,int l,int r,int w,int p){
if(L==l&&R==r){
tree[p].add+=w;
tree[p].sum+=1ll*w*(R-L+1);
return;
}
down(L,R,p);
int mid=L+R>>1;
if(r<=mid)update(L,mid,l,r,w,p<<1);
else if(l>mid)update(mid+1,R,l,r,w,p<<1|1);
else{
update(L,mid,l,mid,w,p<<1);
update(mid+1,R,mid+1,r,w,p<<1|1);
}
up(p);
}
ll query(int L,int R,int l,int r,int p){
if(L==l&&R==r)return tree[p].sum;
down(L,R,p);
int mid=L+R>>1;
if(r<=mid)return query(L,mid,l,r,p<<1);
else if(l>mid)return query(mid+1,R,l,r,p<<1|1);
else return query(L,mid,l,mid,p<<1)+query(mid+1,R,mid+1,r,p<<1|1);
}
}Tree;
up()
实现的线段树问题,参见这儿。