数据结构模板整理

树状数组

单点修改,区间询问

#include<iostream>
#include<cstdio>
using namespace std;
int read()                         //读入优化 
{
    char ch=getchar();
    int a=0,x=1;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') x=-x;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        a=(a<<3)+(a<<1)+(ch-'0');
        ch=getchar();
    }
    return a*x;
}
int n,m,k,x,y;
//n个数,m次操作
//k=1,第x个数加上y
//k=0,问区间[x,y]的和 

int a[500001],c[500001];
int lowbit(int x) //求lowbit { return x&(-x); }
void update(int x,int y) { for(;x<=n;x+=lowbit(x)) c[x]+=y; //第x个数加上y }
int sum(int x) //求区间[1,x]的和 { int ans=0; for(;x;x-=lowbit(x)) ans+=c[x]; return ans; }
int main() { n=read();m=read(); for(int i=1;i<=n;i++) { a[i]=read(); update(i,a[i]); //先建好树 } for(int i=1;i<=m;i++) { k=read();x=read();y=read(); if(k==1) update(x,y); else printf("%d\n",sum(y)-sum(x-1)); //前缀和做差 } return 0; }

 

区间修改,单点询问

#include<iostream>
#include<cstdio>
using namespace std;

int read()                         //读入优化 
{
    char ch=getchar();
    int a=0,x=1;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') x=-x;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        a=(a<<3)+(a<<1)+(ch-'0');
        ch=getchar();
    }
    return a*x;
}

int n,m,k,x,y,v;
//n个数,m次操作
//k=1,区间[x,y]加上v 
//k=2,询问第x个数的值 

int a[500001],c[500001],d[500001];
//a存原数列,c是树状数组,d是差分数组 

int lowbit(int x)                      //求lowbit 
{
    return x&(-x);
}

void update(int x,int y)
{
    for(;x<=n;x+=lowbit(x)) c[x]+=y;   //第x个数加上y 
}

int sum(int x)                         //求区间[1,x]的和 
{
    int ans=0;
    for(;x;x-=lowbit(x)) ans+=c[x];
    return ans;
}

int main()
{
    n=read();m=read();
    
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        d[i]=a[i]-a[i-1];                   //求差分数组 
        update(i,d[i]);                     //建树 
    } 
    
    for(int i=1;i<=m;i++)
    {
        k=read();
        if(k==1)
        {
            x=read();y=read();v=read();     //区间[x,y]加上v 
            update(x,v);                    //差分数组的变化 
            update(y+1,-v);
        }
        else 
        {
            x=read();
            printf("%d\n",sum(x));
        }
    }
    
    return 0;
} 

 

线段树

单点修改,区间询问

#include<iostream>
#include<cstdio>
using namespace std;

int read()                         //读入优化 
{
    char ch=getchar();
    int a=0,x=1;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') x=-x;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        a=(a<<3)+(a<<1)+(ch-'0');
        ch=getchar();
    }
    return a*x;
}

int n,m,k,x,y;
//n个数,m次操作
//k=1,第x个数加上y
//k=2,询问区间[x,y]的和 

int a[500001],sum[500001];
//a是原数列,sum是维护的区间和 

void build(int node,int l,int r)           //建树 
{
    if(l==r)                               //叶子结点 
    {
        sum[node]=a[l];                    //直接赋值 
        return ;
    }
    int mid=(l+r)>>1;
    build(node*2,l,mid);                   //分别建好左右子树 
    build(node*2+1,mid+1,r);
    sum[node]=sum[node*2]+sum[node*2+1];   //加起来就是根结点的区间和 
}

void add(int node,int l,int r,int x,int k) //给第x个数加上k 
{
    if(l==r&&l==x)                         //找到了叶子结点且正好是区间[x,x] 
    {
        sum[node]+=k;
        return ;
    }
    int mid=(l+r)>>1;
    if(x<=mid) add(node*2,l,mid,x,k);      //看是否在左子树里 
    else add(node*2+1,mid+1,r,x,k);        //否则就在右子树里,注意这里能用else是因为这是单点修改   
    sum[node]=sum[node*2]+sum[node*2+1];
}

int ask(int node,int l,int r,int x,int y)  //询问区间和 
{
    if(x<=l&&r<=y) return sum[node];       //[l,r]被完全包含在[x,y]内的话直接返回 
    int mid=(l+r)>>1;
    int rnt=0;
    if(x<=mid) rnt+=ask(node*2,l,mid,x,y); //找左右子树是否有交集 
    if(y>mid) rnt+=ask(node*2+1,mid+1,r,x,y);
    return rnt;
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build(1,1,n);                          //建树 
    for(int i=1;i<=m;i++)
    {
        k=read();x=read();y=read();
        if(k==1) add(1,1,n,x,y);
        else printf("%d\n",ask(1,1,n,x,y)); 
    }
    return 0;
}

 

区间修改,区间询问

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<set>
#include<vector>
#include<map>
#include<queue>

#define N 1000005
#define M 1000005

#define ls (t*2)
#define rs (t*2+1)
#define mid ((l+r)/2) 

using namespace std;

long long i,j,m,n,p,k,lazy[N*4],a[N],x,c,l,r;       //lazy数组存每个结点的懒标记,sum数组存每个结点的区间和,注意四倍空间 

long long ans,sum[N*4];

void build(int l,int r,int t)           //初始化sum的值,为原先的区间和 
{
        if (l==r) sum[t]=a[l];          //只有一个结点 
        else
        {
             build(l,mid,ls);           //找它的左儿子 
             build(mid+1,r,rs);         //找它的右儿子 
             sum[t]=sum[ls]+sum[rs];    //该结点的区间和等于它的左儿子加上它的右儿子的区间和 
        }
}
void down(int t,int len)                //对lazy标记进行下传 
{
        if (!lazy[t]) return;           //如果没有lazy标记,那么直接返回 
        sum[ls]+=lazy[t]*(len-len/2);   //求左儿子的新区间和,len-len/2是左儿子的长度 
        sum[rs]+=lazy[t]*(len/2);       //求右儿子的新区间和,len/2是右儿子的长度 
        lazy[ls]+=lazy[t];              //传给左右儿子,累积lazy标记 
        lazy[rs]+=lazy[t];
        lazy[t]=0;                      //父亲结点的lazy标记已经传下去了,所以要清空 
}

void modify(int ll,int rr,long long c,int l,int r,int t) //[ll,rr]整体加上c,当前节点代表的区间位[l,r] 
{
         if (ll<=l&&r<=rr)              //如果当前分解的区间在所给的区间内 
         {
                sum[t]+=(r-l+1)*c;     //对[l,r]区间的影响就是加上了(r-l+1)*c 
                lazy[t]+=c;             //加上lazy标记 
         }
         else                           //如果当前分解的区间未完全被包含在内的话 
         {
                down(t,r-l+1);         //要往下细分就要下传lazy标记 
                if (ll<=mid) modify(ll,rr,c,l,mid,ls);   //如果与左区间有交集,那么我们就去细分左区间 
                if (rr>mid)  modify(ll,rr,c,mid+1,r,rs); //如果与右区间有交集,那么我们就去细分右区间 
                sum[t]=sum[ls]+sum[rs];//更新一下区间和          
         } 
} 

void ask(long long ll,long long rr,long long l,long long r,long long t) //[ll,rr]是要查询的区间,[l,r]是当前分解的区间 
{
        if (ll<=l&&r<=rr) ans+=sum[t];  //代表着找到了完全被包含在内的一个区间,所以直接返回这个区间的区间和 
        else                            //如果有未完全被包含在内的 
        {
                down(t,r-l+1);          //将lazy标记下传并继续细分它的儿子 
                if (ll<=mid) ask(ll,rr,l,mid,ls);  //如果与左区间有交集就细分左区间 
                if (rr>mid)  ask(ll,rr,mid+1,r,rs);//如果与右区间有交集就细分右区间 
        }
}

int main()
{
        scanf("%d%d",&n,&m); 
        for (i=1;i<=n;++i) scanf("%d",&a[i]); 
        build(1,n,1); 
        for(long long i=1;i<=m;i++)
        {
            long long p;
            scanf("%lld",&p);
            if(p==1)
            {
                   long long l,r,k;
                   scanf("%lld%lld%lld",&l,&r,&k);
                   modify(l,r,k,1,n,1);
            }    
            if(p==2)
            {
                long long l,r;
                scanf("%lld%lld",&l,&r);
                ans=0; 
                ask(l,r,1,n,1); 
                printf("%lld\n",ans);
            }
        } 
        return 0;
}

 

ST表

#include<iostream>
#include<cstdio>
#include<cmath> 
using namespace std;
int a[100001],f[100001][20];
int read()
{
    char ch=getchar();
    long long a=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9')
    {
        a=a*10+(ch-'0');
        ch=getchar();
    }
    return a;
}
int main()
{
    int n,m;
    n=read(),m=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        f[i][0]=a[i];              //初始化 
    }
    for(int j=1;(1<<j)<=n;j++)     //注意j在外层 
       for(int i=1;i+(1<<j)-1<=n;i++)
          f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);  //状态转移方程 
    for(int i=1;i<=m;i++)
    {
        int l=read();
        int r=read();
        int k=(int)(log((double)(r-l+1))/log(2.0)); 
        int ans=max(f[l][k],f[r-(1<<k)+1][k]);
        printf("%d\n",ans);
    }
    return 0;
} 

 

最近公共祖先LCA

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=500001;
int head[2*maxn],to[2*maxn],next[2*maxn],grand[2*maxn][21],dep[maxn];
int n,m,s,edge_sum=0;
void add(int x,int y)          //链表存图 
{
    next[++edge_sum]=head[x];
    head[x]=edge_sum;
    to[edge_sum]=y;
}
void dfs(int v,int deep)      //dfs求出每个点的深度 
{
    dep[v]=deep;
    for(int i=head[v];i>0;i=next[i])
    {
        int u=to[i];
        if(!dep[u]) dfs(u,deep+1),grand[u][0]=v;
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);           //让x的深度大于y 
    for(int i=20;i>=0;i--)                 //跳到同一深度 
       if(dep[y]<=dep[x]-(1<<i)) x=grand[x][i];
    if(x==y) return y;  
    for(int i=20;i>=0;i--)
    {
        if(grand[x][i]!=grand[y][i])       //跳不到同一点就往上跳 
        {
            x=grand[x][i];
            y=grand[y][i];
        }
    }
    return grand[x][0];                    //最后再跳一下肯定是LCA 
}
int read()
{
    char ch=getchar();
    int a=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9')
    {
        a=a*10+(ch-'0');
        ch=getchar();
    }
    return a;
}
int main()
{
    memset(head,0,sizeof(head));
    n=read(),m=read(),s=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        add(x,y);
        add(y,x);
    }
    grand[s][0]=s;                  
    dfs(s,1);
    for(int i=1;(1<<i)<=n;i++)
       for(int j=1;j<=n;j++)
          grand[j][i]=grand[grand[j][i-1]][i-1];    //状态转移方程 
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        printf("%d\n",lca(x,y));
    }
    return 0;
}

 

快读模板

int read()
{
    char ch=getchar();
    int a=0,x=1;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') x=-x;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        a=(a<<3)+(a<<1)+(ch-'0');
        ch=getchar();
    }
    return a*x;
}

 

转载于:https://www.cnblogs.com/xcg123/p/11124349.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值