线段树、最短路径、最小生成树、并查集、二分图匹配、最近公共祖先--C++模板

线段树(区间修改,区间和):

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int c[1000000],n,m;
char s;

void update(int p,int l,int r,int x,int add)
{
    int m=(l+r) / 2;
    if (l==r)
    {
        c[p]+=add;
        return;
    }
    if (x<=m)   update(p*2,l,m,x,add);
    if (x>m)    update(p*2+1,m+1,r,x,add);
    c[p]=c[p<<1]+c[p<<1|1];
}

int find(int p,int l,int r,int a,int b)
{
    if (l==a && r==b)   return c[p];
    else
    {
        int m=(l+r) / 2;
        if (b<=m)   return find(p*2,l,m,a,b);
        else if (a>m)   return find(p*2+1,m+1,r,a,b);
        else return find(p*2,l,m,a,m)+find(p*2+1,m+1,r,m+1,b);
    }
}

int main()
{
    scanf("%d",&n);
    scanf("%d",&m);
    for (int i=1;i<=m;i++)
    {
        cin>>s;
        if (s==‘M‘)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            update(1,1,n,x,y);
        }
        if (s==‘C‘)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%d\n",find(1,1,n,x,y));
        }
    }
}

线段树(lazy):

线段树,并且单纯的线段树会超时,因为在将a到b的点全部加上c时,步骤太多,会超时。

需要优化。即lazy算法;

Lazy:

在将a~b点全部加c时,不要加到每个点,在表示区间的root结构体上增加一个inc域,将要加的值赋给这个inc域,然后就不要再往下了。

在求区间和时,将root中的inc值赋给要求的区间,并且将该节点的root置零。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#define ll long long
using namespace std;
ll a[1000005],t[5000005],n,lazy[5000005],m;

ll read()
{
    ll s=0;
    char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘)    ch=getchar();
    while (ch>=‘0‘ && ch<=‘9‘)
    {
        s=(s << 1) + (s << 3) +ch-‘0‘;ch=getchar();
    }
    return s;
}

void build(ll p,ll l,ll r)
{
    if (l==r)
    {
        t[p]=a[l];
        return;
    }
    ll m=(l+r)>>1;
    build(p<<1,l,m);
    build(p<<1|1,m+1,r);
    t[p]=t[p<<1]+t[p<<1|1];
}

void pushdown(ll x,ll len)
{
    if (lazy[x]!=0)
    {
        lazy[x<<1]+=lazy[x];
        lazy[x<<1|1]+=lazy[x];
        t[x<<1]+=(len-len/2)*lazy[x];
        t[x<<1|1]+=(len/2)*lazy[x];
        lazy[x]=0;
    }
}

void update(ll a,ll b,ll l,ll r,ll x,ll p)
{
    if (a<=l && b>=r)
    {
        t[p]+=(r-l+1)*x;
        lazy[p]+=x;
        return;
    }
    if (lazy[p]!=0) pushdown(p,r-l+1);
    ll m=(l+r)/2;
    if (b<=m)   update(a,b,l,m,x,p<<1);
    else if (a>m)   update(a,b,m+1,r,x,p<<1|1);
    else update(a,m,l,m,x,p<<1),update(m+1,b,m+1,r,x,p<<1|1);
    t[p]=t[p<<1]+t[p<<1|1];
}

ll find(ll a,ll b,ll l,ll r,ll p)
{
    if (lazy[p]!=0) pushdown(p,r-l+1);
    if (a==l && b==r)   return t[p];
    ll m=(l+r)/2;
    if (b<=m)   return find(a,b,l,m,p<<1);
    else if (a>m)   return find(a,b,m+1,r,p<<1|1);
    else    return find(a,m,l,m,p<<1)+find(m+1,b,m+1,r,p<<1|1);
}

int main()
{
    n=read(); m=read();
    for (int i=1;i<=n;i++)
        a[i]=read();
    build(1,1,n);
    int x,y,z;
    for (int i=1;i<=m;i++)
    {
        x=read();
        if (x==1)
        {
            x=read(),y=read(),z=read();
            update(x,y,1,n,z,1);
        }
        else if (x==2)
        {
            y=read(),z=read();
            printf("%lld\n",find(y,z,1,n,1));
        }
    }
}

二分图匹配:

#include <iostream>
#include <cstring>
#include <cstdio>
#define inf 1000007
using namespace std;
struct arr
{
    int y, n;
}f[1000005];
int ls[1000005], n, m, tot, e, ans, link[1000005];
bool b[1000005];

void add(int x, int y)
{
    f[++tot].y = y;
    f[tot].n = ls[x];
    ls[x] = tot;
}

bool find(int x)
{
    for (int i = ls[x]; i; i = f[i].n)
        if (!b[f[i].y])
        {
            b[f[i].y] = 1;
            if (!link[f[i].y] || find(link[f[i].y]))
            {
                link[f[i].y] = x;
                return true;
            }
        }
    return false;
}

void work()
{
    for (int i = 1; i <= n; i++)
    {
        memset(b, 0, sizeof(b));
        if (find(i))    ans++;
    }
}

int main()
{
    int x, y;
    scanf("%d%d%d", &n, &m, &e);
    for (int i = 1; i <= e; i++)
    {
        scanf("%d%d", &x, &y);
        if (y > m) continue;
        add(x, y);
    }
    work();
    printf("%d", ans);
}

最小生成树:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
struct arr
{
    int x,y,w;
}f[200007];
int g[5007],tot,n,m;
bool b[5007];

int cmp(arr a,arr b)
{
    return a.w<b.w;
}

int find(int x)
{
    if (g[x]==x)    return x;
    g[x]=find(g[x]);
    return g[x];
}

int main()
{
    cin>>n>>m;
    int q,p,o,e=0;
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&q,&p,&o);
        e++;
        f[e].x=q;
        f[e].y=p;
        f[e].w=o;
    }
    sort(f+1,f+e+1,cmp);
    for (int i=1;i<=n;i++)
        g[i]=i;
    int k=0;
    for (int i=1;i<=e;i++)
    {
        if (k==n-1) break;
        if (find(g[f[i].x])!=find(g[f[i].y]))
        {
            k++;
            tot+=f[i].w;
            g[find(f[i].x)]=find(f[i].y);
        }
    }
    if (k==n-1) printf("%d",tot);
    else printf("orz");
}

最短路(spfa):

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
struct arr
{
    int to,next,w;
}f[5000007];
int n,m,s,d[1000007],ls[1000007],str[1000007];
bool b[1000007];

void spfa()
{
    for (int i=1;i<=n;i++)
        d[i]=2147483647;
    d[s]=0;
    int head=0,tail=1;
    b[s]=1;
    str[1]=s;
    while (head<tail)
    {
        head++;
        int t=ls[str[head]];
        while (t>0)
        {
            if (d[f[t].to]>d[str[head]]+f[t].w)
            {
                d[f[t].to]=d[str[head]]+f[t].w;
                if (!b[f[t].to])
                {
                    b[f[t].to]=1;
                    tail++;
                    str[tail]=f[t].to;
                }
            }
            t=f[t].next;
        }
        b[str[head]]=0;
    }
}

int main()
{
    scanf("%d%d%d",&n,&m,&s);
    int p,q;
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&q,&p,&f[i].w);
        f[i].to=p;
        f[i].next=ls[q];
        ls[q]=i;
    }

    spfa();
    for (int i=1;i<=n;i++)
        printf("%d ",d[i]);
}

并查集

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n,m,f[10007];

int find(int x)
{
    if (f[x]==x)    return x;
    f[x]=find(f[x]);
    return f[x];
}

int main()
{
    scanf("%d%d",&n,&m);
    int q,p,z;
    for (int i=1;i<=n;i++)
        f[i]=i;
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&z,&q,&p);
        if (z==1)
        {
            f[find(q)]=find(p);
        }
        if (z==2)
        {
            if (find(q)==find(p))   cout<<"Y"<<endl;
            else cout<<"N"<<endl;
        }
    }
}

KMP

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int a[1000005],b[1000005],p[1000005],n,m,next[1000005],ans;
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &p[i]);
        a[i - 1] = p[i] - p[i - 1];
    }
    for (int i = 1; i <= m; i++)
    {
        scanf("%d", &p[i]);
        b[i - 1] = p[i] - p[i - 1];
    }
    int j = 0;
    for (int i = 2; i < m; i++)
    {
        while (j > 0 && b[j + 1] != b[i])   j = next[j];
        if (b[j + 1] == b[i])   j += 1;
        next[i] = j;
    }
    j = 0;
    for (int i = 1; i < n; i++)
    {
        while (j > 0 && b[j + 1] != a[i])   j = next[j];
        if (b[j + 1] == a[i])   j += 1;
        if (j == m - 1)
        {
            ans++;
            j = next[j];
        }
    }
    printf("%d", ans);
}

快速幂

int pow4(int a,int b)
{
  int r=1,base=a;
  while(b)
  {
    if(b&1) r*=base;
    base*=base;
    b>>=1;
  }
  return r;
}

强连通分量(tarjan):

#include <cstdio>
#include <iostream>
using namespace std;
struct arr
{
    int x,y,next;
};
arr f[3000];
int ls[40000],stack[40000],belong[40000],low[40000],dfn[40000],cnt,e,n,d,tot,rd[40000],cd[40000],ans,s1,s2;
bool ins[40000];

void add(int x,int y)
{
    e++;
    f[e].x=x;
    f[e].y=y;
    f[e].next=ls[x];
    ls[x]=e;
}

void tarjan(int i)
{
    int t=0;
    int j=0;
    d++;
    dfn[i]=d;
    low[i]=d;
    tot++;
    stack[tot]=i;
    t=ls[i];
    ins[i]=1;
    while (t>0)
    {
        j=f[t].y;
        if (dfn[j]==0)
        {
            tarjan(j);
            if (low[i]>low[j])  low[i]=low[j];
        }
        else if (ins[j]&&dfn[j]<low[i]) low[i]=dfn[j];
        t=f[t].next;
    }
    if (dfn[i]==low[i])
    {
        cnt++;
        do
        {
            j=stack[tot];
            ins[j]=false;
            tot--;
            belong[j]=cnt;
        }
        while (i!=j);
    }

}

int main()
{
    cin>>n;
    int j;
    e=0;
    for (int i=1;i<=n;i++)
    {
        while (scanf("%d",&j)&&j!=0)
            add(i,j);
    }
    for (int i=1;i<=n;i++)
        if (dfn[i]==0)  tarjan(i);
    for (int i=1;i<=e;i++)
        if (belong[f[i].x]!=belong[f[i].y])
        {
            cd[belong[f[i].x]]++;
            rd[belong[f[i].y]]++;
        }
    ans=0;
    s1=0;
    s2=0;
    for (int i=1;i<=cnt;i++)
    {
        if (rd[i]==0)   {ans++; s1++;}
        if (cd[i]==0)   s2++;
    }
    cout<<ans<<endl;
    if (cnt==1) {s1=0; s2=0;}
    if (s1>s2)  cout<<s1<<endl;
    else cout<<s2<<endl;
}

最短路(floyd):

for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                if (i != j && i != k && j != k)
                    f[i][j] = min(f[i][j], f[i][k] + f[k][j]);

最近公共祖先(lca):

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
struct arr {int  x,y,n;}f[60007];
int ls[30007],n,m,e,deep[30007],fa[30007][30],ans;

void add(int x, int y)
{
    f[++e].x = x;
    f[e].y = y;
    f[e].n = ls[x];
    ls[x] = e;
}

void dfs(int x, int la)
{
    deep[x] = deep[la] + 1;
    fa[x][0] = la;
    for (int i = ls[x]; i ; i = f[i].n)
    {
        if (f[i].y != la)
        {
            dfs(f[i].y, x);
        }
    }
}

int lca(int q, int p)
{
    if (deep[p] < deep[q])  swap(p,q);
    for (int j = 20; j >= 0; j--)
        if (deep[fa[p][j]] >= deep[q])
            p = fa[p][j];
    if (p == q) return p;
    for (int j = 20; j >= 0; j--)
    if (fa[p][j] != fa[q][j])
    {
        p = fa[p][j], q = fa[q][j];
    }
    return fa[p][0];
}

int main()
{
    scanf("%d", &n);
    int x, y;
    for (int i = 1; i <= n - 1; i++)
    {
        scanf("%d%d", &x, &y);
        add(x, y);
        add(y, x);
    }
    dfs(1, 0);
    for (int j = 1; j <= 20; j++)
        for (int i = 1; i <= n; i++)
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
    scanf("%d", &m);
    scanf("%d", &x);
    m--;
    while (m--)
    {
        scanf("%d", &y);
        ans += deep[x] + deep[y] - deep[lca(x, y)] * 2;
        x = y;
    }
    printf("%d", ans);
}

原文地址:https://www.cnblogs.com/jiangxin/p/11840282.html

时间: 2024-10-10 20:47:47

线段树、最短路径、最小生成树、并查集、二分图匹配、最近公共祖先--C++模板的相关文章

bzoj2049 线段树 + 可撤销并查集

https://www.lydsy.com/JudgeOnline/problem.php?id=2049 线段树真神奇 题意:给出一波操作,拆边加边以及询问两点是否联通. 听说常规方法是在线LCT,留坑. 如果说这个删边的操作是删除上一条边,那这自然是可撤销并查集的模板题,直接在线维护就可以了. 但是问题在于删除边的顺序是不可能固定的,要知道并查集是不可以随意撤销的. 万万没想到还有更加高妙的手法. 首先可以证明一条边的存在一定是一段或者多段连续的区间. 建立一条时间节点长度的线段树,结点维护

BZOJ 1854: [Scoi2010]游戏 [连通分量 | 并查集 | 二分图匹配]

题意: 有$n \le 10^6$中物品,每种两个权值$\le 10^4$只能选一个,使得选出的所有权值从1递增,最大递增到多少 一开始想了一个奇怪的规定流量网络流+二分答案做法...然而我还不知道怎么规定流量...并且一定会T 然后发现题解中二分图匹配用了匈牙利,可以从小到大找增广路,貌似比较科学 然后发现还有用并查集的,看到“权值是点,装备是边”后突然灵机一动想到一个dfs做法 每个边的两个点可以选择一个 找出每个连通分量,如果里面有环或重边那么这里面所有点都可以选 如果是树的话,必须放弃一

Wikioi 2492 树状数组+并查集(单点更新区间查询)

刚开始做的时候用线段树做的,然后就跳进坑里了--因为要开方,所以区间的值都得全部变,然后想用lazy标记的,但是发现用不了,单点更新这个用不了,然后就不用了,就T了.然后实在不行了,看了别人的题解,原来是用树状数组+并查集的方法,唉--没想到啊! 因为开方之后多次那个数就会变成1了,所以是1的时候开方下去就没用了.树状数组更新的时候就把其更新的差更新即可,太机智了这题-- 昨天做了,然后出错找了好久都找不出来,原来是把s[i]写成c[i]了,然后答案一直错,晕-- #include <iostr

最小生成树-并查集-Kruskal-zoj-2048-special judge

Highways description The island nation of Flatopia is perfectly flat. Unfortunately, Flatopia has a very poor system of public highways. The Flatopian government is aware of this problem and has already constructed a number of highways connecting som

洛谷P1547 Out of Hay 最小生成树 并查集

洛谷P1547 Out of Hay 最小生成树 并查集 路径压缩 #include <cstdio> #include <cmath> #include <cstdlib> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <iomanip> using namespace std ;

CSP 201703-4 地铁修建【最小生成树+并查集】

问题描述 试题编号: 201703-4 试题名称: 地铁修建 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 A市有n个交通枢纽,其中1号和n号非常重要,为了加强运输能力,A市决定在1号到n号枢纽间修建一条地铁. 地铁由很多段隧道组成,每段隧道连接两个交通枢纽.经过勘探,有m段隧道作为候选,两个交通枢纽之间最多只有一条候选的隧道,没有隧道两端连接着同一个交通枢纽. 现在有n家隧道施工的公司,每段候选的隧道只能由一个公司施工,每家公司施工需要的天数一致.而每家公司最多只能修

ACM1558两线段相交判断和并查集

Segment set Problem Description A segment and all segments which are connected with it compose a segment set. The size of a segment set is the number of segments in it. The problem is to find the size of some segment set. Input In the first line ther

2017端午欢乐赛——Day1T3(树的直径+并查集)

//前些天的和jdfz的神犇们联考的模拟赛.那天上午大概是没睡醒吧,考场上忘了写输出-1的情况,白丢了25分真是**. 题目描述     小C所在的城市有 n 个供电站,m条电线.相连的供电站会形成一个供电群,那么为了节省材料,供电群是一棵树的形式,也即城市是一个森林的形式(树:V个点,V-1条边的无向连通图,森林:若干棵树).每个供电群中不需要所有供电站都工作,最少只需要一个工作,其余的就都会通过电线收到电,从而完成自己的供电任务.当然,这样会产生延迟.定义两个供电站的延迟为它们之间的电线数量

hdu 5458 Stability(树链剖分+并查集)

Stability Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 65535/102400 K (Java/Others)Total Submission(s): 1347    Accepted Submission(s): 319 Problem Description Given an undirected connected graph G with n nodes and m edges, with possibly r

BZOJ 3211 花神游历各国 (树状数组+并查集)

题解:首先,单点修改求区间和可以用树状数组实现,因为开平方很耗时间,所以在这个方面可以优化,我们知道,开平方开几次之后数字就会等于1 ,所以,用数组记录下一个应该开的数,每次直接跳到下一个不是1的数字进行开平方,至于这个数组,可以用并查集维护. #include <cstdio> #include <cmath> #include <iostream> using namespace std; typedef long long LL; LL c[100005]; in