基础算法大全(更新ing

发布时间 2023-05-24 13:10:52作者: o-Sakurajimamai-o
1 前缀和
/// 给定一组数,求任意区间的总和
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,a[N],s[N],m;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        s[i]=s[i-1]+a[i];
    }
    while(m--)
    {
        int l,r;
        cin>>l>>r;
        cout<<s[r]-s[l-1]<<endl;
    }
    return 0;
}
1-2 矩阵的前缀和
思路://https://blog.csdn.net/m0_62881629/article/details/124762394?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168250250916800227440048%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=168250250916800227440048&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-5-124762394-null-null.142^v86^control,239^v2^insert_chatgpt&utm_term=P1387%20%E6%9C%80%E5%A4%A7%E6%AD%A3%E6%96%B9%E5%BD%A2&spm=1018.2226.3001.4187
题目链接://https://www.luogu.com.cn/problem/P1387
///s[i] [j] = s[i-1][j] + s[i][j-1 ] + a[i] [j] - s[i-1][ j-1]
#include<iostream>
#include<cstdio>
using namespace std;
const int N=1010;
int a[N][N],s[N][N];
int main()
{
    int n,m,q;
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
       scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
        s[i][j]=s[i-1][j]+s[i][j-1]+a[i][j]-s[i-1][j-1];
    while(q--)
    {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        printf("%d\n",s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]);
    }
    return 0;
}
洛谷:P1387
#include <bits/stdc++.h>
using namespace std;
int n,m,ans,tot,cnt;
int s[101][101],ma[101][101],sum[101][101];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
      cin>>ma[i][j];
      for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
      sum[i][j]=ma[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];//前缀和
      for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
      {
          int p=i,q=j,tmp=1;//(p,q)为起点 逐步扩展,tmp相当于长度范围.
          if(ma[p][q]==1 && sum[p][q]-sum[i-1][q]-sum[p][j-1]+sum[i-1][j-1]==tmp*tmp && p<=n&&q<=m)
          {
              while(ma[p][q]==1&&sum[p][q]-sum[i-1][q]-sum[p][j-1]+sum[i-1][j-1]==tmp*tmp && p<=n&&q<=m)
                  p++,q++,tmp++;
            ans=max(ans,tmp-1);
         }
      }
    printf("%d\n",ans);
}
2-1 一维差分
///在差分中,b数组是a[i]=b[1]+b[2]+...+b[i];这样的形式叫做差分数组
///也就是a数组是b数组的前缀和;
给出一组数组,要求你在某个区间里加上一个数c,数据有多组
例如:1 2 3 41-2 区间+1,在2-3区间+2,在1-4区间+1;
最终结果是 3 6 6 5
代码实现:
///由于前缀和的存在,如果给b[l]+c,那么a数组从l到n都加上了c,那么如果给b[r+1]-c,这就实现了从l到r的加c形式;
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],b[N];
int n,m;
void Insert(int l,int r,int c)
{
    b[l]+=c;
    b[r+1]-=c;
}
int main()
{
    cin>>n>>m;//n为数组的长度,m为加的次数
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        Insert(i,i,a[i]);///从区间到区间末,加上c,这一步是创建差分数组b
    }
    while(m--)
    {
        int l,r,c;
        cin>>l>>r>>c;
        Insert(l,r,c);///进行操作;
    }
    for(int i=1;i<=n;i++)
        {
            b[i]+=b[i-1];///更新数组;
            cout<<b[i]<<" ";
        }
    return 0;
}
2-2 差分矩阵(二维差分)
与一维差分差不多,和前缀和一样,只是多一个公式
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int a[N][N],b[N][N];
int n,m,q;//nxm的矩阵,q次访问;
void Insert(int x1,int y1,int x2,int y2,int c)
{
    b[x1][y1]+=c;
    b[x2+1][y1]-=c;
    b[x1][y2+1]-=c;
    b[x2+1][y2+1]+=c;
}
int main()
{
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
    {
        cin>>a[i][j];
        Insert(i,j,i,j,a[i][j]);
    }
    while(q--)
    {
        int x1,y1,x2,y2,c;
        cin>>x1>>y1>>x2>>y2>>c;
        Insert(x1,y1,x2,y2,c);
    }
    for(int i=1;i<=n;i++)
       {
            for(int j=1;j<=m;j++)
    {
        b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
        cout<<b[i][j]<<" ";
    }
    cout<<endl;
       }
    return 0;
}
3-1 双指针
求最长不重复序列
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,res=0;
int a[N],s[N];///s用来储存a[i]中的数字出现的次数
int main()
{
    cin>>n;
    for(int i=0;i<n;i++
    cin>>a[i];
    for(int i=0;i<n;i++)
    {
        s[a[i]]++;
        while(s[a[i]]>1)///循环,直到j与i的区间内没有重复元素;
        {
            s[a[j]]--;
            j++;
        }
        res=max(res,i-j+1);
    }
    cout<<res;
    return 0;
}
4-1 离散化
#include<bits/stdc++.h>

using namespace std;

const int N=300010;

typedef pair<int,int>PII;

int n,m;
int a[N],s[N];

vector<int> alls;
vector<PII>add,query;

int find(int x)
{
    int l=0,r=alls.size()-1;
    while(l<r)
    {
        int mid=l+r>>1;
        if(alls[mid]>x) r=mid-1;
        else l=mid+1;
    }
    return l;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        int x,c;
        cin>>x>>c;
        add.push_back({x,c});
        alls.push_back(x);
    }
    for(int i=0;i<m;i++)
    {
        int l,r;
        cin>>l>>r;
        query.push_back({l,r});
        alls.push_back(l);
        alls.push_back(r);
    }
    sort(alls.begin(),alls.end());
    alls.erase(unique(alls.begin(),alls.end()),alls.end());
    for(auto i:add)
    {
        int x=find(i.first);
        a[x]+=i.second;
    }
    for(int i=1;i<=alls.size();i++)
        s[i]=s[i-1]+a[i];
    for(auto i:query)
    {
        int l=find(i .first),r=find(i.second);
        cout<<s[r]-s[l-1]<<endl;
    }
    return 0;
}
5-1 Trie树,快速存储字符串和询问字符串
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int son[N][26],cnt[N],idx;
void Insert(char str[])///插入操作
{
    int p=0;
    for(int i=0;str[i];i++)
    {
        int u=str[i]-'a';
        if(!son[p][u]) son[p][u]=++idx;///如果没有子节点,要自己创建子节点
        p=son[p][u];
    }
    cnt[p]++;
}
int  query(char str[])///询问操作
{
    int p=0;
    for(int i=0;str[i];i++)
    {
        int u=str[i]-'a';
        if(!son[p][u]) return 0;///没有子节点,说明不是这个字符串,返回0;
        p=son[p][u];
    }
    return cnt[p];
}
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        char x;
        cin>>x;
        char y[N];
        cin>>y;
        if(x=='I')
            Insert(y);
        else
        {
           int  res=0;
            res=query(y);
            cout<<res<<endl;
        }
    }
}

6-1 并查集
///find函数可能有点难理解,自己尝试画下图随便理解好吧
///M是合并,Q是询问是否在一个树中
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m;
int q[N];
int find(int x)
{
    if(q[x]!=x) q[x]=find(q[x]));///如果x不是树的头结点,向上循环查找到头结点为止.
    ///可以自己画图理解一下;
    return q[x];
}
int  main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        q[i]=i;
    while(m--)
    {
        char op[2];
        int a,b;
        scanf("%s%d%d",&op,&a,&b);
        if(op[0]=='M') q[find(a)]=find(b);
        else
        {
            if(find(a)==find(b))
                cout<<"YES"<<endl;
            else
                cout<<"NO"<<endl;
        }
    }
    return 0;
}
6-2 查并集,但是可能输出长度
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m;
int p[N],size[N];
int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        p[i]=i;
        size[i]=1;
    }
    while(m--)
    {
        ///C为合并,Q1为 检查是否在同一区间,Q2检查长度;
        char op[2];
        scanf("%s",&op);
        int a,b;
        if(op[0]=='C')
        {
            cin>>a>>b;
            if(find(a)==find(b)) continue;///特判
            size[find(b)]+=size[find(a)];
            p[find(a)]=find(b);
        }
        else if(op[1]=='1')
        {
            cin>>a>>b;
            if(find(a)==find(b))
                cout<<"YES"<<endl;
            else
                cout<<"No"<<endl;
        }
        else if(op[1]=='2')
        {
            cin>>a;
            cout<<size[find(a)]<<endl;
        }
    }
}
6-3 食物链
https://www.luogu.com.cn/problem/P2024
///这题是真难,看视频加理解一共花了俩小时
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m,res=0;
int p[N],d[N];
int find(int x)
{
    if(p[x]!=x)///与以往不同,这里的d数组刚开始的作用是,x节点与父节点的距离,经过find函数递归之后
                    ///d数组变成了,x节点到头节点的距离,这样就可以来判断是不是同一类和接下来做题了
    {
        int t=find(p[x]);
        d[x]+=d[p[x]];///因为要用到p[x],所以先储存;
        p[x]=t;
    }
    return p[x];
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        p[i]=i;
    while(m--)
    {
        int a,b,c;
        cin>>c>>a>>b;
        if(a>n||b>n) res++;
        else
        {
            if(c==1)
            {
                int pa=find (a),pb=find(b);
                if(pa==pb&&(d[a]-d[b])%3) res++;///3个一循环;如果A和B是同类,而且他俩已经在合并过了,那么两者的距离相减,如果余三不得0那么就是错的;
                else
                {
                    p[pa]=pb;
                    d[pa]=d[b]-d[a];
                }
            }
            else
            {
                int pa=find(a),pb=find(b);
                if(pa==pb&&(d[a]-d[b]-1)%3) res++;
                else if(pa!=pb)
                {
                    p[pa]=pb;
                    d[pa]=d[b]+1-d[a];
                }
            }
        }
    }
    cout<<res;
    return 0;
}
6-4 自动程序检测装置
https://www.luogu.com.cn/problem/P1955
///这个题是我得知有查并集这个算法的起初
///查并集+离散化,题目i和j都开到10的九次方了,不用离散化等着爆吧
///本题思路
#include<bits/stdc++.h>
using namespace std;
const int N=400010;
int t,n;
int a[2*N],p[N],d[2*N],b[N],c[N];
int find(int x)///
{
    if(p[x]!=x)
        p[x]=find(p[x]);
    return p[x];
}
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a[2*i-1]>>a[2*i]>>c[i];
            d[2*i-1]=a[2*i-1],d[2*i]=a[2*i];
        }
    sort(d+1,d+2*n+1);
    int h=0;
    b[0]=-1;
    for(int i=1;i<=2*n;i++)
        if(b[h]!=d[i]) b[++h]=d[i];///离散化完成,用了两次这个离散化模板了,感觉yxc老师的那个离散化模板用不了一点
    for(int i=1;i<=h;i++)
        p[i]=i;///更新爹节点
    for(int i=1;i<=n;i++)
    {
        if(c[i]==0)
            continue;
        int x=lower_bound(b+1,b+h+1,a[2*i-1])-b;
        int y=lower_bound(b+1,b+h+1,a[2*i])-b;
        if(find(x)==find(y)) continue;
        else
            p[find(x)]=find(y);///将所有相等的元素,放进树里面;接下来的就是狠狠的查找有没有内鬼
    }
    bool f=false;
    for(int i=1;i<=n;i++)
    {
        if(c[i]==1)
            continue;
        int x=lower_bound(b+1,b+h+1,a[2*i-1])-b;
        int y=lower_bound(b+1,b+h+1,a[2*i])-b;
        if(find(x)==find(y))///查找内鬼,如果有人在相等的树里面,就是内鬼!
        {
            cout<<"NO"<<endl;
            f=true;
            break;
        }
    }
    if(!f)
        cout<<"YES"<<endl;
}
    return 0;
}
7-0 利用动态二维数组创建无权邻接表
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
vector<int>g[N];
int n,m;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        cin>>x>>y;
        g[x].push_back(y);
        g[y],push_back(x);
    }
    for(int i=1;i<=n;i++)
    {
        cout<<i<<":"<<" "<<endl;
        for(auto v:g[i])
            cout<<v<<" ";
        cout<<endl;
    }
}
7-1 创立有权邻接表
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m;
struct node
{
    int to;
    int val;
};
vector<node>g[N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        node tmp;
        cin>>a>>b>>c;
        tmp.to=b;
        tmp.val=c;
        g[a].push_back(tmp);
    }
    for(int i=1;i<=n;i++)
    {
        cout<<i<<":"<<endl;
        for(auto v:g[i])
            cout<<v.to<<" "<<v.val;
        cout<<endl;
    }
    return 0;
}