算法模版总结

解题技巧

快读快输

攻略引用

#include <bits/stdc++.h>
using namespace std;
int read()
{
    char c=getchar();
    int f=1,x=0;
    while(!isdigit(c))//c<48||c>57
    {
        if(c==45)//c=='-'
            f=-1;
        c=getchar();
    }
    while(isdigit(c))
    {
        x=(x<<3)+(x<<1)+(c^48);
        c=getchar();
    }
    return f*x;
}
void write(int x)
{
    if(x==0)
    {
        putchar(48);
        return ;
    }
    if(x<0)
        putchar(45),x=-x;
    int l=0;
    char _c[25];
    while(x)
    {
        _c[++l]=(x%10)^48;
        x/=10;
    }
    for(int i=l;i>=1;i--)
    {
        putchar(_c[i]);
    }
    return ;
}
int solve1()//快读快输 
{
    int n=read(),a,b=0;
    for(int i=1;i<=n;i++)
    {
        a=read();
        b^=a;
    }
    for(int i=1;i<=n;i++)
    {
        write(b);
        putchar(10);
    }
    return 0;
}
int solve2()//cin、cout解除捆绑和同步 
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n,a,b=0;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a;
        b^=a;
    }
    for(int i=1;i<=n;i++)
        cout<<b<<'\n';
    return 0;
}
int main()
{
    return solve1();//618ms
//    return solve2();//496ms
}

离散化

注:此代码并非解题代码

#include <bits/stdc++.h>
using namespace std;
const int N=100001;
int n,a[N];
vector<int>lsh;
void Discretization()//离散化
{
    for(int i=1;i<=n;i++)
    {
        lsh.push_back(a[i]);
    }
    sort(lsh.begin(),lsh.end());
    lsh.erase(unique(lsh.begin(),lsh.end()),lsh.end());
    for(int i=1;i<=n;i++)
    {
        a[i]=lower_bound(lsh.begin(),lsh.end(),a[i])-lsh.begin();//最小数字从0开始 
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    Discretization();
    for(int i=1;i<=n;i++)
    {
        printf("%d ",a[i]);
    }
    return 0;
}

数学

埃氏筛法

#include <bits/stdc++.h>
using namespace std;
int n,q;
bool book[100000001];
vector<int>pr;
void prime()
{
    book[0]=book[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(book[i]==0)
        {
            pr.push_back(i);
            for(int j=i*2;j<=n;j+=i)
                book[j]=1;
        }
    } 
}
int main()
{
    scanf("%d%d",&n,&q);
    prime();
    int a;
    for(int i=1;i<=q;i++)
    {
        scanf("%d",&a);
        printf("%d\n",pr[a-1]);
    }
    return 0;
}

线性筛法

#include <bits/stdc++.h>
using namespace std;
int n,q;
bool book[100000001];
vector<int>pr;
void prime()
{
    book[0]=book[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(book[i]==0)
        {
            pr.push_back(i);
        }
        for(int j=0;j<pr.size();j++)
        { 
            if(i*pr[j]>n)
                break;
            book[i*pr[j]]=1;
            if(i%pr[j]==0)
                break;/*保证一个数只被它的最小质因数筛掉 
            满足条件时,可证明:
            设pr[s]<pr[j]<pr[l](s<j<l) 
            1.i的最小质因数是pr[j](如果是之前的质数,早就break了)
            2.i*pr[s]的最小质因数是pr[s](如果是更小的质数,在那时会因为1结论break) 
            3.i*pr[l]的最小值质因数是pr[j](i的最小质因数是pr[j],pr[j]<pr[l],所以成立) 
            */
        }
    } 
}
int main()
{
    scanf("%d%d",&n,&q);
    prime();
    int a;
    for(int i=1;i<=q;i++)
    {
        scanf("%d",&a);
        printf("%d\n",pr[a-1]);
    }
    return 0;
}

快速幂

#include <bits/stdc++.h>
using namespace std;
long long my_pow(long long a,int b,long long mod)//计算a^b%mod的值 
{
    long long ans=1;
    while(b)
    {
        if(b&1)ans=(ans*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return ans;
}
int main()
{
    long long a,mod;
    int b;
    scanf("%lld%d%lld",&a,&b,&mod);
    printf("%lld^%d mod %lld=%lld",a,b,mod,my_pow(a,b,mod));
    return 0;
}

图论

图的存储

注:此代码并非解题代码

#include <bits/stdc++.h>
using namespace std;
const int N=1001,M=100001;
//法1:邻接矩阵 
int e1[N][N];//存储边权值,没有可用1代替 
//法2:邻接表
int head[N],nxt[2*M],v[2*M],w[2*M],d[N],tot1=0;//无向图需2*M的空间 
void add(int a,int b,int w1)
{
    v[++tot1]=b;
    w[tot1]=w1;//边权值,没有可不写
    nxt[tot1]=head[a];
    head[a]=tot1;
    d[a]++;//求度数 
    return ;
}
//法3:vector
struct node2
{
    int v;
    int w;//边权值,没有可不写 
    node2(int v,int w):v(v),w(w){}
};
vector<node2> e2[N];
//法4:边集数组
struct node3
{
    int u,v;
    int w;//边权值,没有可不写 
    node3(int u,int v,int w):u(u),v(v),w(w){}
    node3():u(0),v(0),w(0){}
}e3[2*M];//无向图需2*M的空间 
int tot2=0;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int a,b;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        //领接矩阵存图 
        e1[a][b]=1;//有向图只用存这一条边
        e1[b][a]=1;
        //邻接表存图 
        add(a,b,1);//有向图只用存这一条边
        add(b,a,1);
        //vector存图
        e2[a].push_back(node2(b,1));//有向图只用存这一条边
        e2[b].push_back(node2(a,1));
        //边集数组存图
        e3[++tot2]=node3(a,b,1);//有向图只用存这一条边
        e3[++tot2]=node3(b,a,1);
    }
    //邻接矩阵输出 
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
            printf("%d ",e1[i][j]);
        printf("\n"); 
    }
    //领接表输出
    for(int i=1;i<=n;i++)
    {
        printf("%d ",d[i]);//输出度数 
        for(int j=head[i];j;j=nxt[j])
        {
            printf("%d ",v[j]);
        }
        printf("\n");
    } 
    //vector输出
    for(int i=1;i<=n;i++)
    {
        printf("%d ",e2[i].size());//输出度数
        for(int j=0;j<e2[i].size();j++)
        {
            printf("%d ",e2[i][j].v);
        }
        printf("\n");
    }
    //边集数组输出
    for(int i=1;i<=tot2;i++)
    {
        printf("%d %d %d\n",e3[i].u,e3[i].v,e3[i].w);
    }
    return 0;
}

dijkstra

普通的dijkstra

//O(n^2)
#include <bits/stdc++.h>
using namespace std;
struct node
{
    int v,w;
    node(int v,int w):v(v),w(w){}
};
int n,m,s,dis[100001],book[100001];
vector<node>e[100001];
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    int u,v,w;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        e[u].push_back(node(v,w));
    }
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    for(int i=1;i<=n;i++)
    {
        int minn=0x3f3f3f3f,minp;
        for(int j=1;j<=n;j++)
        {
            if((!book[j])&&dis[j]<minn)
            {
                minn=dis[j];
                minp=j;
            }
        }
        if(minn==0x3f3f3f3f)
            break;
        book[minp]=1;
        for(int j=0;j<e[minp].size();j++)
        {
            if(dis[e[minp][j].v]>dis[minp]+e[minp][j].w&&(!book[e[minp][j].v]))
            {
                dis[e[minp][j].v]=dis[minp]+e[minp][j].w;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(dis[i]==0x3f3f3f3f)
            printf("2147483647 ");
        else
            printf("%d ",dis[i]);
    }
    return 0;
}

堆优化的dijkstra

//O(n*log2n)
#include <bits/stdc++.h>
using namespace std;
struct node
{
    int v,w;
    node(int v,int w):v(v),w(w){}
};
struct node2
{
    int d,p;
    node2(int d,int p):d(d),p(p){}
};
bool operator<(node2 x,node2 y)
{
    return x.d>y.d;
}
int n,m,s,dis[100001],book[100001];
vector<node>e[100001];
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    int u,v,w;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        e[u].push_back(node(v,w));
    }
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    priority_queue<node2>pq;
    pq.push(node2(dis[s],s));
    while(!pq.empty())
    {
        int tp=pq.top().p;
        pq.pop();
        if(book[tp])continue;
        book[tp]=1;
        for(int j=0;j<e[tp].size();j++)
        {
            if(dis[e[tp][j].v]>dis[tp]+e[tp][j].w&&(!book[e[tp][j].v]))
            {
                dis[e[tp][j].v]=dis[tp]+e[tp][j].w;
                pq.push(node2(dis[e[tp][j].v],e[tp][j].v));
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(dis[i]==0x3f3f3f3f)
            printf("2147483647 ");
        else
            printf("%d ",dis[i]);
    }
    return 0;
}

bellman-ford

//O(n*m)
#include <bits/stdc++.h>
using namespace std;
struct node
{
    int u,v,w;
}e[500001];
int n,m,s,dis[100001];
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    int u,v,w;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    }
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    bool flag;
    for(int i=1;i<=n-1;i++)//进行n-1轮松弛,每一轮松弛所得到的最短路可经过n-1条边 
    {
        flag=0;
        for(int j=1;j<=m;j++)//用m条边松弛 
        {
            if(dis[e[j].v]>dis[e[j].u]+e[j].w)
            {
                dis[e[j].v]=dis[e[j].u]+e[j].w;
                flag=1;
            } 
        } 
        if(!flag)
            break;//松弛不了了直接退出 
    }
    for(int i=1;i<=n;i++)
    {
        if(dis[i]==0x3f3f3f3f)
            printf("2147483647 ");
        else
            printf("%d ",dis[i]);
    }
    return 0;
}

SPFA(队列优化的bellman-ford)

//最坏O(n*m)
#include <bits/stdc++.h>
using namespace std;
struct node
{
    int v,w;
    node(int v,int w):v(v),w(w){}
};
vector<node> e[100001];
int n,m,s,dis[100001],book[100001];
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    int u,v,w;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        e[u].push_back(node(v,w));
    }
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    queue<int>q;
    q.push(s);
    book[s]=1;
    while(!q.empty())//只松弛上一轮松弛成功过的点 
    {
        u=q.front(); 
        q.pop();
        book[u]=0;
        for(int j=0;j<e[u].size();j++)//用这个点的出边松弛 
        {
            if(dis[e[u][j].v]>dis[u]+e[u][j].w)
            {
                dis[e[u][j].v]=dis[u]+e[u][j].w;
                if(!book[e[u][j].v])
                {
                    q.push(e[u][j].v);
                    book[e[u][j].v]=1;
                }
            } 
        } 
    }
    for(int i=1;i<=n;i++)
    {
        if(dis[i]==0x3f3f3f3f)
            printf("2147483647 ");
        else
            printf("%d ",dis[i]);
    }
    return 0;
}

floyd

#include <bits/stdc++.h>
using namespace std;
int n,m,e[101][101];
int main()
{
    scanf("%d%d",&n,&m);
    int u,v,w;
    for(int i=1;i<=n;i++)//一定要初始化 
    {
        for(int j=1;j<=n;j++)
        {
            if(i==j)e[i][j]=0;
            else e[i][j]=0x3f3f3f3f;
        }
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        e[u][v]=w;
        e[v][u]=w;
    }
    for(int k=1;k<=n;k++)//中转点
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(e[i][j]>e[i][k]+e[k][j])
                {
                    e[i][j]=e[i][k]+e[k][j];
                }
            }
        } 
    } 
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            printf("%d ",e[i][j]);
        }
        printf("\n");
    } 
    return 0;
}

最小生成树

kurskal

//O((n+m)*log2n)
#include <bits/stdc++.h>
using namespace std;
struct node
{
    int u,v,w;
}e[400001];//双向边,两倍存 
int n,m,f[5001];
bool cmp(node x,node y)
{
    return x.w<y.w;
}
int findf(int x)
{
    if(x==f[x])return x;
    f[x]=findf(f[x]);
    return f[x];
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        f[i]=i;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    }
    sort(e+1,e+m+1,cmp);
    int cnt=0,sum=0;
    int fu,fv;
    for(int i=1;i<=m;i++)
    {
        fu=findf(e[i].u),fv=findf(e[i].v);
        if(fu!=fv)
        {
            cnt++;
            sum+=e[i].w;
            f[fv]=fu;
        }
    }
    if(cnt<n-1)
        printf("orz");
    else
        printf("%d",sum);
    return 0;
}

prim

普通prim

//O(n^2)
#include <bits/stdc++.h>
using namespace std;
struct node
{
    int v,w;
    node(int v,int w):v(v),w(w){}
};
int n,m,dis[5001],book[5001];
vector<node>e[5001];
int main()
{
    scanf("%d%d",&n,&m);
    int u,v,w;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        e[u].push_back(node(v,w));
        e[v].push_back(node(u,w)); 
    }
    for(int i=1;i<=n;i++)
        dis[i]=0x3f3f3f3f;
    dis[1]=0;//默认以1号点为根 
    for(int i=1;i<=n;i++)//每轮增加一个节点,共n个 
    {
        int minn=0x3f3f3f3f,minp; 
        for(int j=1;j<=n;j++)
        {
            if(dis[j]<minn&&book[j]==0)
            {
                minn=dis[j];
                minp=j;
            }
        }
        book[minp]=1;
        for(int j=0;j<e[minp].size();j++)
        {
            if(dis[e[minp][j].v]>e[minp][j].w&&book[e[minp][j].v]==0)
            {
                dis[e[minp][j].v]=e[minp][j].w;
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(dis[i]==0x3f3f3f3f)
        {
            printf("orz");
            return 0;
        }
        ans+=dis[i];
    }
    printf("%d",ans);
    return 0;
}

堆优化prim

//O(n*log2n)
#include <bits/stdc++.h>
using namespace std;
struct node
{
    int p,di;
    node(int p,int di):p(p),di(di){}
};
int head[5001],nxt[400001],v[400001],w[400001],tot;
int n,m,dis[5001],book[5001];
bool operator<(node x,node y)
{
    return x.di>y.di;
}
void add(int a,int b,int c)
{
    ++tot;
    v[tot]=b,w[tot]=c;
    nxt[tot]=head[a];
    head[a]=tot;
}
int main()
{
    scanf("%d%d",&n,&m);
    int a,b,c;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    memset(dis,0x3f3f3f3f,sizeof(dis));
    int ans=0,cnt=0;
    priority_queue<node>pq;
    dis[1]=0;
    pq.push(node(1,dis[1]));
    while(!pq.empty())
    {
        int u=pq.top().p;
        pq.pop();
        if(book[u])continue;
        book[u]=1;
        ans+=dis[u];
        cnt++;
        for(int i=head[u];i;i=nxt[i])
        {
            if(dis[v[i]]>w[i]&&book[v[i]]==0)
            {
                dis[v[i]]=w[i];
                pq.push(node(v[i],dis[v[i]]));
            }
        }
    }
    if(cnt<n-1)
        printf("orz");
    else
        printf("%d",ans);
    return 0;
}

tarjan

求边双连通分量/割边

  • 链接

    #include <bits/stdc++.h>
    using namespace std;
    const int N=500005,M=4000005;
    int n,m;
    int dfn[N],low[N],id=0,cutedge[M];
    int book[N];
    vector<vector<int> >ebc;
    //###邻接表###
    int head[N],nxt[M],to[M],tot=1;
    //小技巧:从2开始存边,通过异或操作可以取反向边((2n)^1=2n+1,(2n+1)^1=2n) 
    void add(int u,int v)
    {
      tot++;
      to[tot]=v;
      nxt[tot]=head[u];
      head[u]=tot;
    }
    //############
    void tarjan(int u,int in_edge)//求割边的tarjan
    {
      dfn[u]=low[u]=++id;//首次访问时间戳和能通过自己及孩子访问到的最小首访时间戳都初始为下一个时间戳 
      for(int i=head[u];i;i=nxt[i])
      {
          if(dfn[to[i]]==0)//若它的邻接点没有访问过,则它是孩子 
          {
              tarjan(to[i],i);
              //在回溯时,求解割边
              if(dfn[u]<low[to[i]])//对于边u-v,若v不能访问到u的祖先或u自己,则该边为割边
                  cutedge[i]=cutedge[i^1]=1;//标记该边及反向边为割边
              low[u]=min(low[u],low[to[i]]);//将孩子的最小访问信息传递给父亲 
          } 
          else if(i!=(in_edge^1))//若该边不是通向u的边,则这是一条返祖边 
          {
               low[u]=min(low[u],dfn[to[i]]);//把祖先to[i]的信息传递给u 
          }
      } 
      return ;
    } 
    void dfs(int u,int idd)
    {
      book[u]=idd;//标记u点在这个双连通分量中
      ebc.back().push_back(u);//将u点加入到这个双连通分量中 
      for(int i=head[u];i;i=nxt[i])
      {
          if(book[to[i]]||cutedge[i])continue;//如果邻接点已经统计过,或者经过了割边,则不统计入
          dfs(to[i],idd); 
      }
    } 
    int main()
    {
      cin>>n>>m;
      int _u,_v;
      for(int i=1;i<=m;i++)
      {
          cin>>_u>>_v;
          add(_u,_v);
          add(_v,_u);
      }
      for(int i=1;i<=n;i++)//每个点都要走到 
      {
          if(dfn[i]==0)//走过就不走了 
              tarjan(i,0);
      } 
      int ans=0;
      for(int i=1;i<=n;i++)//求取边双连通分量 
      {
          if(book[i]==0)//如果该点还未在一个双连通分量中
          {
              ebc.push_back(vector<int>());//新建一个空的双连通分量 
              dfs(i,++ans);//求取包含这个节点的双连通分量 
          } 
      }
      cout<<ans<<'\n';
      for(int i=0;i<ebc.size();i++)
      {
          cout<<ebc[i].size()<<' ';
          for(int j=0;j<ebc[i].size();j++)
          {
              cout<<ebc[i][j]<<' ';
          }
          cout<<'\n';
      }
      return 0;
    }
    

树论

最近公共祖先

在线算法(倍增)

#include <bits/stdc++.h>
using namespace std;
int n,m,s,deep[500001],t[25][500001],lgn;
vector<int>e[500001];
void dfs(int u,int f,int dp)
{
    deep[u]=dp;
    t[0][u]=f;
    for(int i=0;i<e[u].size();i++)
    {
        if(e[u][i]==f)
            continue;
        dfs(e[u][i],u,dp+1);
    }
}
int lca(int x,int y)
{
    if(deep[x]>deep[y])
        swap(x,y);
    for(int i=lgn;i>=0;i--)
    {
        if(deep[x]<=deep[t[i][y]])
            y=t[i][y];
    }
    if(x==y)
        return x;
    for(int i=lgn;i>=0;i--)
    {
        if(t[i][x]!=t[i][y])
            x=t[i][x],y=t[i][y];
    }
    return t[0][x];
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    int u,v;
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(s,s,1);
    lgn=log2(n);
    for(int i=1;i<=lgn;i++)
    {
        for(int j=1;j<=n;j++)
        {
            t[i][j]=t[i-1][t[i-1][j]];
        }
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        printf("%d\n",lca(u,v));
    }
    return 0;
}

数据结构

二叉堆

#include <bits/stdc++.h>
using namespace std;
const int N=1000001;
int tree[N],size=0;
void push(int x)//插入 O(log2n)
{
    tree[++size]=x;
    x=size;
    if(x==1)
        return ;
    int _t;
    //实现最小堆(最大堆只需要变一下符号) 
    while(x!=1&&tree[x]<tree[x/2])
    {
        _t=tree[x];
        tree[x]=tree[x/2];
        tree[x/2]=_t;
        x=x/2;
    }
    return ;
}
void pop_front()//弹出堆顶 O(log2n)
{
    if(!size)
        return ;
    tree[1]=tree[size--];
    int x=1,minn,p,_t;
    while(x*2<=size||x*2+1<=size)
    {
        minn=tree[x];
        if(x*2<=size&&tree[x*2]<minn)
            minn=tree[x*2],p=x*2;
        if(x*2+1<=size&&tree[x*2+1]<minn)
            minn=tree[x*2+1],p=x*2+1;
        if(minn==tree[x])
            return ;
        tree[p]=tree[x];
        tree[x]=minn;
        x=p;
    }
    return ;
} 
inline int top()//询问堆顶 O(1) 
{
    return size?tree[1]:-1;
}
int main()
{
    int n,op,x;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d",&x);
            push(x);
        }
        else if(op==2)
        {
            if(top()!=-1)
                printf("%d\n",top());
        }
        else if(op==3)
        {
            pop_front();
        }
    }
    return 0;
}

线段树

区间查询、区间修改

  • 普通写法

    #include <bits/stdc++.h>
    using namespace std;
    const int N=100005;
    long long nums[N];
    struct Tree
    {
      long long data;
      int l,r;
    }tree[4*N];
    long long modified[4*N];
    void build_tree(long long *num,int l,int r,int x)//建树 
    {
      tree[x].l=l;tree[x].r=r;modified[x]=0;
      if(tree[x].l==tree[x].r)
      {
          tree[x].data=num[tree[x].l];//不要写成了num[x]
          return ;
      }
      int mid=(l+r)>>1; 
      build_tree(num,l,mid,x<<1);
      build_tree(num,mid+1,r,x<<1|1);
      tree[x].data=tree[x<<1].data+tree[x<<1|1].data;
      return ;
    }
    void push_down(int x)
    {
      tree[x<<1].data+=(tree[x<<1].r-tree[x<<1].l+1)*modified[x];
      tree[x<<1|1].data+=(tree[x<<1|1].r-tree[x<<1|1].l+1)*modified[x];
      modified[x<<1]+=modified[x];
      modified[x<<1|1]+=modified[x];
      modified[x]=0;
      return ;
    } 
    void interval_modification(int l,int r,int x,long long modify_number)//区间修改 
    {
      if(l<=tree[x].l&&tree[x].r<=r)//不用向下 
      {
          tree[x].data+=(tree[x].r-tree[x].l+1)*modify_number;
          modified[x]+=modify_number;
          return ; 
      }
      push_down(x);//修改时不下方会导致求和错误 
      int mid=(tree[x].l+tree[x].r)>>1;
      if(l<=mid)//修改区间在左半区间有
           interval_modification(l,r,x<<1,modify_number); 
      if(r>=mid+1)//修改区间在右半区间有
           interval_modification(l,r,x<<1|1,modify_number); 
      tree[x].data=tree[x<<1].data+tree[x<<1|1].data;
      return ; 
    }
    long long interval_query(int l,int r,int x)//区间查询 
    {
      if(l<=tree[x].l&&tree[x].r<=r)//不用向下 
      {
          return tree[x].data; 
      }
      push_down(x);//下放操作 
      int mid=(tree[x].l+tree[x].r)>>1;
      long long res=0; 
      if(l<=mid)//查询区间在左半区间有
           res+=interval_query(l,r,x<<1); 
      if(r>=mid+1)//查询区间在右半区间有
           res+=interval_query(l,r,x<<1|1); 
      return res;
    }
    int main()
    {
      int n,m;
      cin>>n>>m; 
      for(int i=1;i<=n;i++)
      {
          cin>>nums[i];
      }
      build_tree(nums,1,n,1);
      int op,x,y;
      long long k;
      for(int i=1;i<=m;i++)
      {
          cin>>op;
          if(op==1)
          {
              cin>>x>>y>>k;
              interval_modification(x,y,1,k);
          }
          else if(op==2)
          {
              cin>>x>>y;
              cout<<interval_query(x,y,1)<<'\n';
          }
      }
      return 0;
    }
    
  • 模板类写法(本地报错)

    #include <bits/stdc++.h>
    #define N 100005
    using namespace std;
    template <typename mytype> class Segment_tree{
    private:
      struct Tree{
          mytype data;
          int l,r;
      }tree[4*N];
      mytype modified[4*N];
      void push_down(int x){
          tree[x<<1].data+=(tree[x<<1].r-tree[x<<1].l+1)*modified[x];
          tree[x<<1|1].data+=(tree[x<<1|1].r-tree[x<<1|1].l+1)*modified[x];
          modified[x<<1]+=modified[x];
          modified[x<<1|1]+=modified[x];
          modified[x]=0;
          return ;
      } 
    public:
      void build_tree(mytype *num,int l,int r,int x){//建树 
    
          tree[x].l=l;tree[x].r=r;modified[x]=0;
          if(tree[x].l==tree[x].r)
          {
              tree[x].data=num[tree[x].l];//不要写成了num[x]
              return ;
          }
          int mid=(l+r)>>1; 
          build_tree(num,l,mid,x<<1);
          build_tree(num,mid+1,r,x<<1|1);
          tree[x].data=tree[x<<1].data+tree[x<<1|1].data;
          return ;
      }
      void interval_modification(int l,int r,int x,mytype modify_number){//区间修改 
    
          if(l<=tree[x].l&&tree[x].r<=r)//不用向下 
          {
              tree[x].data+=(tree[x].r-tree[x].l+1)*modify_number;
              modified[x]+=modify_number;
              return ; 
          }
          push_down(x);//修改时不下方会导致求和错误 
          int mid=(tree[x].l+tree[x].r)>>1;
          if(l<=mid)//修改区间在左半区间有
               interval_modification(l,r,x<<1,modify_number); 
          if(r>=mid+1)//修改区间在右半区间有
               interval_modification(l,r,x<<1|1,modify_number); 
          tree[x].data=tree[x<<1].data+tree[x<<1|1].data;
          return ; 
      }
      mytype interval_query(int l,int r,int x){//区间查询 
    
          if(l<=tree[x].l&&tree[x].r<=r)//不用向下 
          {
              return tree[x].data; 
          }
          push_down(x);//下放操作 
          int mid=(tree[x].l+tree[x].r)>>1;
          mytype res=0; 
          if(l<=mid)//查询区间在左半区间有
               res+=interval_query(l,r,x<<1); 
          if(r>=mid+1)//查询区间在右半区间有
               res+=interval_query(l,r,x<<1|1); 
          return res;
      }
    };
    long long nums[N];
    int main()
    {
      int n,m;
      cin>>n>>m; 
      for(int i=1;i<=n;i++)
      {
          cin>>nums[i];
      }
      Segment_tree<long long>stree;
      stree.build_tree(nums,1,n,1);
      int op,x,y;
      long long k;
      for(int i=1;i<=m;i++)
      {
          cin>>op;
          if(op==1)
          {
              cin>>x>>y>>k;
              stree.interval_modification(x,y,1,k);
          }
          else if(op==2)
          {
              cin>>x>>y;
              cout<<stree.interval_query(x,y,1)<<'\n';
          }
      }
      return 0;
    }
    

树状数组

单点修改,区间查询

  • 普通写法
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x)&(-x))
#define N 500005
int tree[N],n;
void build(int *num,int x)
{
    int lb;
    n=x;
    for(int i=1;i<=n;i++)
    {
        tree[i]+=num[i];
        lb=i+lowbit(i);
        if(lb<=n)tree[lb]+=tree[i];
    }
    return ;
}
void Single_point_modification(int x,int modify_number)//单点修改 
{
    for(;x<=n;x+=lowbit(x))
        tree[x]+=modify_number;
    return ;
}
int Prefix_query(int x)//前缀查询 
{
    int sum=0;
    for(;x;x-=lowbit(x))
        sum+=tree[x];
    return sum;
}
int Interval_query(int l,int r)//区间查询 
{
    return Prefix_query(r)-Prefix_query(l-1);
}
int nums[N];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>nums[i];
    }
    build(nums,n);
    int op,x,y;
    for(int i=1;i<=m;i++)
    {
        cin>>op>>x>>y;
        if(op==1)
        {
            Single_point_modification(x,y);
        }
        else if(op==2)
        {
            cout<<Interval_query(x,y)<<'\n';
        }
    }
    return 0;
}
  • 模板类写法
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x)&(-x))
#define N 500005
template<typename mytype> class Binary_Index_Tree{
private:
    mytype tree[N];
    int n;
public:
    void build(mytype *num,int x)
    {
        int lb;
        n=x;
        memset(tree,0,sizeof(tree));
        for(int i=1;i<=n;i++)
        {
            tree[i]+=num[i];
            lb=i+lowbit(i);
            if(lb<=n)tree[lb]+=tree[i];
        }
        return ;
    }
    void Single_point_modification(int x,mytype modify_number)//单点修改 
    {
        for(;x<=n;x+=lowbit(x))
            tree[x]+=modify_number;
        return ;
    }
    mytype Prefix_query(int x)//前缀查询 
    {
        mytype sum=0;
        for(;x;x-=lowbit(x))
            sum+=tree[x];
        return sum;
    }
    mytype Interval_query(int l,int r)//区间查询 
    {
        return Prefix_query(r)-Prefix_query(l-1);
    }
};
int nums[N];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>nums[i];
    }
    Binary_Index_Tree<int>bitree;
    bitree.build(nums,n);
    int op,x,y;
    for(int i=1;i<=m;i++)
    {
        cin>>op>>x>>y;
        if(op==1)
        {
            bitree.Single_point_modification(x,y);
        }
        else if(op==2)
        {
            cout<<bitree.Interval_query(x,y)<<'\n';
        }
    }
    return 0;
}

区间修改,单点查询

  • 模板类写法

    #include <bits/stdc++.h>
    using namespace std;
    #define lowbit(x) ((x)&(-x))
    #define N 500005
    template<typename mytype> class Binary_Index_Tree{
    private:
      mytype tree[N];
      int n;
    public:
      void build(mytype *num,int x)
      {
          int lb;
          n=x;
          memset(tree,0,sizeof(tree));
          for(int i=1;i<=n;i++)
          {
              tree[i]+=num[i];
              lb=i+lowbit(i);
              if(lb<=n)tree[lb]+=tree[i];
          }
          return ;
      }
      void Single_point_modification(int x,mytype modify_number)
      {
          for(;x<=n;x+=lowbit(x))
              tree[x]+=modify_number;
          return ;
      }
      mytype Prefix_query(int x)
      {
          mytype sum=0;
          for(;x;x-=lowbit(x))
              sum+=tree[x];
          return sum;
      }
      mytype Interval_query(int l,int r)
      {
          return Prefix_query(r)-Prefix_query(l-1);
      }
    };
    int nums[N];
    int main()
    {
      int n,m;
      cin>>n>>m;
      for(int i=1;i<=n;i++)
      {
          cin>>nums[i];
      }
      nums[n+1]-=nums[n];
      for(int i=n;i>=1;i--)
      {
          nums[i]-=nums[i-1];//技巧:使用差分,转化操作 
      }
      Binary_Index_Tree<int>bitree;
      bitree.build(nums,n+1);
      int op,x,y,k;
      for(int i=1;i<=m;i++)
      {
          cin>>op;
          if(op==1)
          {
              cin>>x>>y>>k;
              //差分,把x-y增加k (x及以后的所有增加k,(y+1)及以后的减少k还原) 
              bitree.Single_point_modification(x,k);
              bitree.Single_point_modification(y+1,-k);
          }
          else if(op==2)
          {
              cin>>x;
              cout<<bitree.Prefix_query(x)<<'\n';//差分的前缀和等于原数 
          }
      }
      return 0;
    }
    
posted @ 2024-08-03 20:14  mike_666  阅读(15)  评论(0)    收藏  举报