提高组OI的一些实用模板

本文整理了OI竞赛中常用的字符串匹配算法KMP及其扩展,AC自动机,以及解决区间查询问题的模板,如RMQ、LCA等。通过这些模板,可以帮助参赛者快速解决相关问题。

【模板】KMP字符串匹配

题目描述

如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置。
为了减少骗分的情况,接下来还要输出子串的前缀数组next。(如果你不知道这是什么意思也不要问,去百度搜[kmp算法]学习一下就知道了。)

输入格式

第一行为一个字符串,即为s1
第二行为一个字符串,即为s2

输出格式

若干行,每行包含一个整数,表示s2在s1中出现的位置
接下来1行,包括length(s2)个整数,表示前缀数组next[i]的值。

输入输出样例

输入 #1

ABABABC
ABA

输出 #1

1
3
0 0 1

代码

By _皎月半洒花

#include<iostream>
#include<cstring>
#define MAXN 1000010
using namespace std;
int kmp[MAXN];
int la,lb,j; 
char a[MAXN],b[MAXN];
int main()
{
    cin>>a+1;
    cin>>b+1;
    la=strlen(a+1);
    lb=strlen(b+1);
    for (int i=2;i<=lb;i++)
       {     
       while(j&&b[i]!=b[j+1])
        j=kmp[j];    
       if(b[j+1]==b[i])j++;    
        kmp[i]=j;
       }
    j=0;
    for(int i=1;i<=la;i++)
       {
          while(j>0&&b[j+1]!=a[i])
           j=kmp[j];
          if (b[j+1]==a[i]) 
           j++;
          if (j==lb) {cout<<i-lb+1<<endl;j=kmp[j];}
       }

    for (int i=1;i<=lb;i++)
    cout<<kmp[i]<<" ";
    return 0;
}

【模板】扩展 KMP

题目描述

有两个字符串aaa,bbb,要求输出bbb与aaa的每一个后缀的最长公共前缀

输入格式

两行,分别为两个字符串aaa,bbb

输出格式

共两行
第一行有lenb个数,为b的next数组(特别地, n e x t 1 next_{1} next1​为lenb)
第二行有lena个数,即答案

输入输出样例

输入 #1

aaaabaa
aaaaa

输出 #1

5 4 3 2 1
4 3 2 1 0 2 1

代码

By 20181gdgzoi236_lc

#include<bits/stdc++.h>

#define N 1000010 

using namespace std;

int q,nxt[N],extend[N];
string s,t;

void getnxt()
{
    nxt[0]=t.size();//nxt[0]一定是T的长度
    int now=0;
    while(t[now]==t[1+now]&&now+1<(int)t.size())now++;//这就是从1开始暴力
    nxt[1]=now;
    int p0=1;
    for(int i=2;i<(int)t.size();i++)
    {
        if(i+nxt[i-p0]<nxt[p0]+p0)nxt[i]=nxt[i-p0];//第一种情况
        else
        {//第二种情况
            int now=nxt[p0]+p0-i;
            now=max(now,0);//这里是为了防止i>p的情况
            while(t[now]==t[i+now]&&i+now<(int)t.size())now++;//暴力
            nxt[i]=now;
            p0=i;//更新p0
        }
    }
}

void exkmp()
{
    getnxt();
    int now=0;
    while(s[now]==t[now]&&now<min((int)s.size(),(int)t.size()))now++;//暴力
    extend[0]=now;
    int p0=0;
    for(int i=1;i<(int)s.size();i++)
    {
        if(i+nxt[i-p0]<extend[p0]+p0)extend[i]=nxt[i-p0];//第一种情况
        else
        {//第二种情况
            int now=extend[p0]+p0-i;
            now=max(now,0);//这里是为了防止i>p的情况
            while(t[now]==s[i+now]&&now<(int)t.size()&&now+i<(int)s.size())now++;//暴力
            extend[i]=now;
            p0=i;//更新p0
        }
    }
}

int main()
{
    cin>>s>>t;
    exkmp();
    int len=t.size();
    for(int i=0;i<len;i++)printf("%d ",nxt[i]);//输出nxt
    puts("");
    len=s.size();
    for(int i=0;i<len;i++)printf("%d ",extend[i]);//输出extend
    return 0;
}

【模板】AC自动机

题目描述

给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过。

输入格式

第一行一个n,表示模式串个数;
下面n行每行一个模式串;
下面一行一个文本串。

输出格式

一个数表示答案

输入输出样例

输入 #1

2
a
aa
aa

输出 #1

2

代码

By zcysky

#include<bits/stdc++.h>
#define N 500010
using namespace std;
queue<int>q;
struct Aho_Corasick_Automaton{
    int c[N][26],val[N],fail[N],cnt;
    void ins(char *s){
        int len=strlen(s);int now=0;
        for(int i=0;i<len;i++){
            int v=s[i]-'a';
            if(!c[now][v])c[now][v]=++cnt;
            now=c[now][v];
        }
        val[now]++;
    }
    void build(){
        for(int i=0;i<26;i++)if(c[0][i])fail[c[0][i]]=0,q.push(c[0][i]);
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=0;i<26;i++)
            if(c[u][i])fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]);
            else c[u][i]=c[fail[u]][i];
        }
    }
    int query(char *s){
        int len=strlen(s);int now=0,ans=0;
        for(int i=0;i<len;i++){
            now=c[now][s[i]-'a'];
            for(int t=now;t&&~val[t];t=fail[t])ans+=val[t],val[t]=-1;
        }
        return ans;
    }
}AC;
int n;char p[1000005];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%s",p),AC.ins(p);
    AC.build();
    scanf("%s",p);int ans=AC.query(p);
    printf("%d\n",ans);
    return 0;
}

[HAOI2006]受欢迎的牛|【模板】强连通分量

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

输入格式

第一行:两个用空格分开的整数:N和M
第二行到第M + 1行:每行两个用空格分开的整数:A和B,表示A喜欢B
输出格式
第一行:单独一个整数,表示明星奶牛的数量

输入输出样例

输入 #1

3 3
1 2
2 1
2 3

输出 #1

1

代码

By 来日方长

    #include<bits/stdc++.h>
    #define N 10050
    using namespace std;
    struct EDGE{
        int next,to;
    }edge[N*20];
    int head[20*N],dfn[N],low[N];
    int du[N],id[N],all[N];
    bool insta[N];int cnt,tot,gg,n,m;
    stack<int>s;
    inline void add(int x,int y){
        cnt++;
        edge[cnt].to=y;
        edge[cnt].next=head[x];
        head[x]=cnt;
    }
    void in(int &read){
        int x=0,f=1;char ch;
        for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
        if(ch=='-'){f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        read=x*f;//可以处理负数的读入优化
    }
    void tarjan(int x){
        dfn[x]=low[x]=++tot;
        s.push(x);insta[x]=true;
        for(int i=head[x];i;i=edge[i].next){
            int u=edge[i].to;
            if(!dfn[u]){
                tarjan(u);
                low[x]=min(low[x],low[u]);
            }
            else if(insta[u])low[x]=min(low[x],dfn[u]);
        }//tarjan模板
        int k;
        if(low[x]==dfn[x]){
            ++gg;
            do{
                k=s.top();s.pop();
                insta[k]=false;
                id[k]=gg;all[gg]++;//将一个分量中的元素染成一色
            }while(x!=k);
        }
    }
    int main(){
        in(n);in(m);
        int a,b;
        for(register int i=1;i<=m;i++){
            in(a);in(b);
            add(a,b);
        }
        for(register int i=1;i<=n;i++)
            if(!dfn[i])tarjan(i);
        for(register int w=1;w<=n;w++){
            for(int i=head[w];i;i=edge[i].next){
                int u=edge[i].to;
                if(id[w]!=id[u]){
                    du[id[w]]++;//遍历每一个点并记录出度
                }
            }
        }
        int tt=0;
        for(register int i=1;i<=gg;i++)
            if(!du[i]){
            if(tt){puts("0");return 0;}//两次出现出度为0直接输出0
            tt=i;//记录出度为零的分量的边号
        }
        printf("%d\n",all[tt]);
        return 0;
}

RMQ模板

By 田益铭

求区间的最大值和最小值

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;
const int MAXN = 100117;
int n,query;
int num[MAXN];
 
int F_Min[MAXN][20],F_Max[MAXN][20];
 
void Init()
{
    for(int i = 1; i <= n; i++)
    {
        F_Min[i][0] = F_Max[i][0] = num[i];
    }
 
    for(int i = 1; (1<<i) <= n; i++)  //按区间长度递增顺序递推
    {
        for(int j = 1; j+(1<<i)-1 <= n; j++)  //区间起点
        {
            F_Max[j][i] = max(F_Max[j][i-1],F_Max[j+(1<<(i-1))][i-1]);
            F_Min[j][i] = min(F_Min[j][i-1],F_Min[j+(1<<(i-1))][i-1]);
        }
    }
}
 
int Query_max(int l,int r)
{
    int k = (int)(log(double(r-l+1))/log((double)2));
    return max(F_Max[l][k], F_Max[r-(1<<k)+1][k]);
}
 
int Query_min(int l,int r)
{
    int k = (int)(log(double(r-l+1))/log((double)2));
    return min(F_Min[l][k], F_Min[r-(1<<k)+1][k]);
}
 
int main()
{
    int a,b;
    scanf("%d %d",&n,&query);
    for(int i = 1; i <= n; i++)
        scanf("%d",&num[i]);
    Init();
    while(query--)
    {
        scanf("%d %d",&a,&b);
        printf("区间%d到%d的最大值为:%d\n",a,b,Query_max(a,b));
        printf("区间%d到%d的最小值为:%d\n",a,b,Query_min(a,b));
        printf("区间%d到%d的最大值和最小值只差为:%d\n",a,b,Query_max(a,b)-Query_min(a,b));
    }
    return 0;
}

求区间内出现次数最多的数字出现的次数

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
 
const int maxn = 100017;
int num[maxn], f[maxn], MAX[maxn][20];
int n;
int max(int a,int b)
{
    return a>b ? a:b;
}
int rmq_max(int l,int r)
{
    if(l > r)
        return 0;
    int k = log((double)(r-l+1))/log(2.0);
    return max(MAX[l][k],MAX[r-(1<<k)+1][k]);
}
void init()
{
    for(int i = 1; i <= n; i++)
    {
        MAX[i][0] = f[i];
    }
    int k = log((double)(n+1))/log(2.0);
    for(int i = 1; i <= k; i++)
    {
        for(int j = 1; j+(1<<i)-1 <= n; j++)
        {
            MAX[j][i] = max(MAX[j][i-1],MAX[j+(1<<(i-1))][i-1]);
        }
    }
}
int main()
{
    int a, b, q;
    while(scanf("%d",&n) && n)
    {
        scanf("%d",&q);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d",&num[i]);
        }
        sort(num+1,num+n+1);
        for(int i = 1; i <= n; i++)
        {
            if(i == 1)
            {
                f[i] = 1;
                continue;
            }
            if(num[i] == num[i-1])
            {
                f[i] = f[i-1]+1;
            }
            else
            {
                f[i] = 1;
            }
 
        }
 
        init();
 
        for(int i = 1; i <= q; i++)
        {
            scanf("%d%d",&a,&b);
            int t = a;
            while(t<=b && num[t]==num[t-1])
            {
                t++;
            }
            int cnt = rmq_max(t,b);
            int ans = max(t-a,cnt);
            printf("%d\n",ans);
        }
    }
    return 0;
}
/*
10 3
-1 -1 1 2 1 1 1 10 10 10
2 3
1 10
5 10
*/

倍增求LCA

题目描述

如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入格式

第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。
接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。
接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。

输出格式

输出包含M行,每行包含一个正整数,依次为每一个询问的结果。

输入输出样例

输入 #1

5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5

输出 #1

4
4
1
4
4

代码

By 北极熊

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=500000+2;
int n,m,s;
int k=0;
int head[maxn],d[maxn],p[maxn][21];//head数组就是链接表标配了吧?d存的是深度(deep),p[i][j]存的[i]向上走2的j次方那么长的路径
struct node{
    int v,next;
}e[maxn*2];//存树
void add(int u,int v)
{
    e[k].v=v;
    e[k].next=head[u];
    head[u]=k++;
}               //加边函数
void dfs(int u,int fa)
{
    d[u]=d[fa]+1;
    p[u][0]=fa;
    for(int i=1;(1<<i)<=d[u];i++)
        p[u][i]=p[p[u][i-1]][i-1];
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].v;
        if(v!=fa)
            dfs(v,u);
    }
}                               //首先进行的预处理,将所有点的deep和p的初始值dfs出来
int lca(int a,int b)                                          //非常标准的lca查找
{
    if(d[a]>d[b])
        swap(a,b);           //保证a是在b结点上方,即a的深度小于b的深度
    for(int i=20;i>=0;i--)
        if(d[a]<=d[b]-(1<<i))
            b=p[b][i];             //先把b移到和a同一个深度
    if(a==b)
        return a;                 //特判,如果b上来和就和a一样了,那就可以直接返回答案了
    for(int i=20;i>=0;i--)
    {
        if(p[a][i]==p[b][i])
            continue;
        else
            a=p[a][i],b=p[b][i];           //A和B一起上移
    }
    return p[a][0];               找出最后a值的数字
}
int main()
{
    memset(head,-1,sizeof(head));
    int a,b;
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);                      //无向图,要加两次
    }
    dfs(s,0);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        printf("%d\n",lca(a,b));
    }
    return 0;
}

组合数取模

By _奶酪

#include<iostream>  
#include<cstdio>  
#include<ctime>  
#include<cstring>  
#include<cstdlib>  
#include<vector>  
#define LL  __int64 
using namespace std;  
LL PowMod(LL a,LL b,LL MOD){  //费马小定理求逆元
    LL ret=1;  
    while(b){  
        if(b&1) ret=(ret*a)%MOD;  
        a=(a*a)%MOD;  
        b>>=1;  
    }  
    return ret;  
}  
LL fac[100005];  
LL Get_Fact(LL p){  
    fac[0]=1;  
    for(LL i=1;i<=p;i++)  
        fac[i]=(fac[i-1]*i)%p;  //预处理阶乘 
}  
LL Lucas(LL n,LL m,LL p){  
    LL ret=1;  
    while(n&&m){  
        LL a=n%p,b=m%p;  
        if(a<b) return 0;  
        ret=(ret*fac[a]*PowMod(fac[b]*fac[a-b]%p,p-2,p))%p;  
        n/=p;  
        m/=p;  
    }  
    return ret;  
}  
int main(){  
    int t;  
    scanf("%d",&t);  
    while(t--){  
        LL n,m,p;  
        scanf("%I64d%I64d%I64d",&n,&m,&p);  
        Get_Fact(p);  
       
        printf("%I64d\n",Lucas(n,m,p));  
    }  
    return 0;  
}  
/*
卢卡斯定理
O(logp(n)) 
*/

平衡树

By _皎月半洒花

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
using namespace std;
#define MAXN 1000000
int f[MAXN],cnt[MAXN],value[MAXN],sons[MAXN][2],sub_size[MAXN],whole_size,root;                 
inline int qread(){
    int res=0,k=1;
    char c=getchar();
    while(!isdigit(c)){
        if(c=='-')k=-1;
        c=getchar();
    }
    while(isdigit(c)){
        res=(res<<1)+(res<<3)+c-48;
        c=getchar();
    }
    return res*k;
}
inline void S_Clear(int x){
    sons[x][0]=sons[x][1]=f[x]=sub_size[x]=cnt[x]=value[x]=0; 
}
inline bool get_which(int x){
    return sons[f[x]][1]==x;
}
inline void update(int x){
    if (x){  
        sub_size[x]=cnt[x];  
        if (sons[x][0]) sub_size[x]+=sub_size[sons[x][0]];  
        if (sons[x][1]) sub_size[x]+=sub_size[sons[x][1]];  
    }  
    return ;
}
inline void rotate(int x){
    int father=f[x],g_father=f[father],which_son=get_which(x);
    sons[father][which_son]=sons[x][which_son^1];
    f[sons[father][which_son]]=father;
    sons[x][which_son^1]=father;
    f[father]=x;
    f[x]=g_father;
    if(g_father){
        sons[g_father][sons[g_father][1]==father]=x;
    }
    update(father);
    update(x);
}
inline void splay(int x){
    for (int fa;fa=f[x];rotate(x))  
      if (f[fa])  
        rotate((get_which(x)==get_which(fa))?fa:x);  
    root=x;  
}
inline void insert(int x){
    if(!root){
        whole_size++;
        sons[whole_size][0]=sons[whole_size][1]=f[whole_size]=0;
        root=whole_size;
        sub_size[whole_size]=cnt[whole_size]++;
        value[whole_size]=x;
        return ;
    } 
    int now=root,fa=0;
    while(1){
        if(x==value[now]){
            cnt[now]++;
            update(now);
            update(fa);
            splay(now);
            break;
        }
        fa=now;
        now=sons[now][value[now]<x];
        if(!now){
            whole_size++;
            sons[whole_size][0]=sons[whole_size][1]=0;
            f[whole_size]=fa;
            sub_size[whole_size]=cnt[whole_size]=1;
            sons[fa][value[fa]<x]=whole_size;
            value[whole_size]=x;
            update(fa);
            splay(whole_size);
            break; 
        }
    }

}
inline int find_num(int x){ 
    int now=root;
    while(1){
        if(sons[now][0]&&x<=sub_size[sons[now][0]])
        now=sons[now][0];
        else {
            int temp=(sons[now][0]?sub_size[sons[now][0]]:0)+cnt[now];
            if(x<=temp)return value[now];
            x-=temp;
            now=sons[now][1];
        }
    }
}

inline int find_rank(int x){
      int now=root,ans=0;  
    while(1){  
        if (x<value[now])  
          now=sons[now][0];  
        else{  
            ans+=(sons[now][0]?sub_size[sons[now][0]]:0);  
            if (x==value[now]){  
                splay(now); return ans+1;  
            }  
            ans+=cnt[now];  
            now=sons[now][1];  
        }  
    }  
}
inline int find_pre(){
    int now=sons[root][0];
    while(sons[now][1])now=sons[now][1];
    return now;
}
inline int find_suffix(){
    int now=sons[root][1];
    while(sons[now][0])now=sons[now][0];
    return now;
}
inline void my_delete(int x){
    int hhh=find_rank(x);
    if (cnt[root]>1){
    cnt[root]--; 
    update(root); 
    return;
    }  
    if (!sons[root][0]&&!sons[root][1]) {
    S_Clear(root);
    root=0;
    return;
    }  
    if (!sons[root][0]){  
        int old_root=root; 
        root=sons[root][1];
        f[root]=0; 
        S_Clear(old_root); 
        return;  
    }  

    else if (!sons[root][1]){  
        int old_root=root; 
        root=sons[root][0]; 
        f[root]=0; 
        S_Clear(old_root); 
        return;  
    } 
    int left_max=find_pre(),old_root=root;  
    splay(left_max);  
    sons[root][1]=sons[old_root][1];  
    f[sons[old_root][1]]=root;  
    S_Clear(old_root);  
    update(root);  
}

int main(){
    int m,num,be_dealt;
    cin>>m;
    for(int i=1;i<=m;i++){
       num=qread();
       be_dealt=qread();
        switch(num)
        {
            case 1:insert(be_dealt);break;
            case 2:my_delete(be_dealt);break;
            case 3:printf("%d\n",find_rank(be_dealt));break;
            case 4:printf("%d\n",find_num(be_dealt));break;
            case 5:insert(be_dealt);printf("%d\n",value[find_pre()]);my_delete(be_dealt);break;
            case 6:insert(be_dealt);printf("%d\n",value[find_suffix()]);my_delete(be_dealt);break;
        }
    }
    return 0;
}

线段树

zhhe0101

/*不怕比我聪明的人,只怕比我聪明但比我还要努力的人*/
#include<iostream>
#include<cstdio>
#define INF 99999999
using namespace std;
typedef long long LL; 
struct node
{
    LL val;
    LL len;
    LL lazy;
    LL l,r;
}tree[300005];
LL arr[500005];
LL n,m;
void build(LL root,LL l,LL r)  //建树 
{
    LL mid;
    tree[root].lazy=0;
    tree[root].l=l;tree[root].r=r;
    tree[root].len=r-l+1;
    if (l==r) tree[root].val=arr[l];
    else
    {
        mid=(l+r)/2;
        build(root*2,l,mid);
        build(root*2+1,mid+1,r);
        tree[root].val=tree[root*2].val+tree[root*2+1].val;
    }
}
void pushdown(LL root)  //向下传递lazy标记 
{
    if (tree[root].lazy)
    {
        tree[root*2].lazy+=tree[root].lazy;
        tree[root*2+1].lazy+=tree[root].lazy;
        tree[root*2].val+=tree[root*2].len*tree[root].lazy;
        tree[root*2+1].val+=tree[root*2+1].len*tree[root].lazy;
        tree[root].lazy=0; 
    }
}
void add(LL root,LL id,LL addval)  //单点更新 
{
    LL mid;
    if (tree[root].l==tree[root].r)
    {
        tree[root].val+=addval;
        return;
    }
    else
    {
        mid=(tree[root].l+tree[root].r)/2;
        if (id<=mid) add(root*2,id,addval);
        else add(root*2+1,id,addval);
        tree[root].val=tree[root*2].val+tree[root*2+1].val;
    }
}
LL query(LL root,LL l,LL r)  //计算区间和 
{
    LL mid;
    if (tree[root].l>=l&&tree[root].r<=r)
        return tree[root].val;
    if (tree[root].l>r||tree[root].r<l)
        return 0;
    if (tree[root].lazy) pushdown(root);
    return query(root*2,l,r)+query(root*2+1,l,r);
}
void update(LL root,LL l,LL r,LL addval)  //区间更新 
{
    LL mid;
    if (tree[root].l>=l&&tree[root].r<=r)
    {
        tree[root].lazy+=addval;
        tree[root].val+=tree[root].len*addval;
        return;
    }
    if (tree[root].l>r||tree[root].r<l)
        return;
    if (tree[root].lazy) pushdown(root);
    update(root*2,l,r,addval);
    update(root*2+1,l,r,addval);
    tree[root].val=tree[root*2].val+tree[root*2+1].val;
}
int main()
{
    LL i,x,y,z,k;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++)
    {
        scanf("%lld",&arr[i]);
    }
    build(1,1,n);
    for (i=1;i<=m;i++)
    {
        scanf("%lld",&z);
        if (z==1)
        {
            scanf("%lld%lld%lld",&x,&y,&k);
            update(1,x,y,k);
        }
        else 
        {
            scanf("%lld%lld",&x,&y);
            printf("%lld\n",query(1,x,y));
        }
    }
}

只需要在建树的时候注意tree[].val存储的是每个节点区间的最大值或最小值即可求区间最大/最小值。

int ask(int root,int l,int r)
{
    int mid;
    if (tree[root].l==l&&tree[root].r==r)
        return tree[root].val;
    else 
    {
        mid=(tree[root].l+tree[root].r)/2;
        if (mid>=r)
            return ask(root*2,l,r);
        else if (mid<l)
            return ask(root*2+1,l,r); 
        else return ask(root*2,l,mid)+ask(root*2+1,mid+1,r);

    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值