冲刺NOIp2016算法模板

数据结构

int strack[maxn];
int head;
bool b[maxn];

void push(int x)
{
    strack[++head]=x;
    b[x]=true;
};

int pop()
{
    int ret;
    ret=strack[head--];
    b[ret]=false;
    return ret;
};

bool empty()
{
    return head>0;
}

队列

int queue[2*maxn];
int tail,head;
bool b[maxn];

void push(int x)
{
    queue[++tail]=x;
    bool[x]=true;
};

int pop()
{
    int ret;
    ret=queue[++head];
    b[ret]=false;
    return ret;
};

bool empty()
{
    return head>=tail;
};  

当然有的时候你手写的数据结构需要比较大的空间,这样队列就会造成很多损失,所以相应的就有两种解决方法:
一:STL;
二:循环队列,只需改两个地方(代码如下);

head=(head+1)%n+1;//把head++改
tail=(tail+1)%n+1;//把tail++改

树状数组

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int sum[500005];
inline int  lowbit (int x)
{
    return x&-x;
}
int add (int k,int a)
{
    while (k<=n)
    {
        sum[k]+=a;
        k+=lowbit(k);
    }
}
int out (int k)
{
    int ans=0;
    while(k)
    {
        ans+=sum[k];
        k-=lowbit(k);
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    int a,b;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a);
        add(i,a);
    }
    for(int i=1;i<=m;i++)
    {
       scanf("%d",&a);
       if (a==1)  
       {
        scanf("%d%d",&a,&b);
        add (a,b);
       }
       else  
       {
        scanf("%d%d",&a,&b);
        printf("%d\n",out(b)-out(a-1));
       }
    }
return 0;
}
应用模型:树状数组求逆序对
void update(int n)
{
    while(n<=maxn)
    {
        c[n]+=1;
        n+=lowbit(n);
    };
};

main():
for(int i = 1; i <= n; ++i)  //主程序里面加上这个
{  
    update(reflect[i]);  
    ans += i - getsum(reflect[i]);//reflect是离散化后的数组
} 

STL

关于每个STL我只会写一下是什么,怎么用(举例子的形式),不会说的太细
Vector

不定长度数组

#include <vector>
vector<int> first;  //第一种定义方法

int myints[]={16,2,77,29};
vector<int> second(myints,myints+4);//第二种定义方法

sort(second.begin(),second.end());//对vector排序

a=second[i];//可以这么使用

//以下是对vector的操作

Vector<int> opt;

opt.begin();    //返回起始地址
opt.end();  //返回结束地址
opt.size(); //返回大小
opt.empty();    //返回是否vector为空
opt.back(); //返回最后一个push进的数
opt.pop_back(); //把最后一个数弹出(不返回)
opt.push_back(int x);//把x从后面push进去

opt.erase(opt.begin(),opt.begin()+3);//删掉前三个元素
opt.erase(opt.begin()+5);//删掉第6个元素

Queue

队列,操作与Stack一样。
Priority_queue
相当于堆

#include <queue>

priority_queue<int> Bigheap;//定义一个大根堆
priority_queue<int,vector<int>,greater<int> > Smallheap;//定义一个小根对(注意两个尖括号间有空格)

//以下是操作
priority_queue<int> opt;
opt.top();//返回堆顶元素的值(不弹出)
opt.pop();//弹出堆顶元素(无返回值)
opt.push(x);
Stack
stack<int> opt;
opt.front();//返回
opt.size();
opt.empty();
opt.push();
opt.pop();//弹出

Deque

双向队,操作与Stack一样

Bitset

压位神器,只普及一下,不会用。

Set

set<int> first;
int myints[]= {10,20,30,40,50};
set<int> second (myints,myints+5);
set<int> third (second);
set<int> fourth (second.begin(), second.end());
third.rbegin(); third.rend();//rend相当于begin,rbegin相当于end
third.size();//返回大小
third.insert(60);
third.erase(it);
third.erase(50);//删除元素'50'
third.find(10);//找元素'10'
third.lower_bound(30); third.upper_bound(30);//'30'出现的第一个位置/最后一个位置
third.clear();//清除

Multiset

与Set用法一样,只是允许重复元素。

Map

map<char,int> first;
first[‘a’] = 10;
first.insert(make_pair(‘b’,20));
it++; ++it; it--; --it;
first.erase(1);//删除元素
firstt.count(1);//看有没有关系

Algorithm里其他好用的函数

Next_permutation
int a[]={1,2,3,4};
next_permutation(a,a+3);//下一个全排列
//现在a数组变成了:1 2 4 3
Lower_bound与Upper_bound
a=lower_bound(first,last,val)-a;//有返回值
upper_bound(first,last,val);
Merge
merge (first,first+5,second,second+5,v.begin(),compare);
sort
bool compare(int a,int b)
{
    return a<b;
};//compare函数的例子
sort(起始地址,结束地址,compare函数);
Reverse

Reverse(myvector.begin(),myvector.end());

Unique
bool myfunction (int i, int j)
{
  return (i==j);
}

unique(起始地址,结束地址,去重条件函数);//按照函数里面编写的规则去重,当然也可以没有第三个参数

数论

快速幂

计算 basen mod p

long long fast(long long base,long long n,long long p)
{
    long long s=1;
    while(n)
    {
        if(n&1)
            s=(s*base)%p;
        base=(base*base)%p;
        n=n>>1;
    }
    return s;
}

矩阵快速幂

#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define mod=1e9+7;
using namespace std;
long long n,k;
struct sq
{
    long long a[110][110];
    sq(){memset(a,0,sizeof(a));}
};

void pre(sq &x)
{
    for (long long i=1;i<=n;i++)
        x.a[i][i]=1;
}

sq operator * (const sq &x,const sq &y){
    sq c;
    for (long long i=1;i<=n;i++)
      for (long long j=1;j<=n;j++)
       for (long long p=1;p<=n;p++)
        c.a[i][j]=(c.a[i][j]+x.a[i][p]*y.a[p][j])%mod;
    return c;
}
int main(){
    cin>>n>>k;
    sq base;
    for (long long i=1;i<=n;i++){
        for (long long j=1;j<=n;j++){
            scanf("%lld",&base.a[i][j]);
        }
    }
    sq ans;
    pre(ans);
    while (k)
    {
        if (k&1) ans=ans*base;
        k>>=1;
        base=base*base;
    }
    for (long long i=1;i<=n;i++)
    {
        for (long long j=1;j<=n;j++)
            printf("%lld ",ans.a[i][j]);
        printf("\n");
    }
return 0;
}

筛法求素数 :欧拉筛法

 f[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!f[i])
          p[++num]=i;
        for(int j=1;j<=num;j++)
        {
            if(p[j]*i>n)break;
            f[p[j]*i]=1;
            if(i%p[j]==0)break;
        }
    }

最大公约数和最小公倍数

gcd为最大公约数,lcm为最小公倍数
a∗b=gcd∗lcm;

int gcd(int a,int b)//注意此处a要大于b
{
    return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b)
{
    return a*b/gcd(a,b);
}

扩展欧几里德

求ax+by=c的特解<=>求 axc(modb)

int x,y;
int exgcd(int a,int b)
{
    int ret,t;
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ret=gcd(b,a%b);
    t=y; y=x-(a%b)*y; x=t;
    return ret;
} 

欧拉函数(PHI φ)

高效算法同时打出prime表与phi表,比单独用筛法打出phi表与prime表快3倍左右

void prime_phi_table()
{
    int i,j;
    phi[1] = 1;
    for(i = 2;i <= MAXN;i++)
    {
        if(!vis[i])
        {
            prime[++cnt] = i;
            phi[i] = i-1;
        }
        for(j = 1;j <= cnt && i * prime[j] <= MAXN;j++)
        {
            vis[i * prime[j]] = true;
            if(i % prime[j] == 0)
            {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            else phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }
}

求n的欧拉函数值

int phi(int n)  
{  
    int t = sqrt(n + 0.5);  
    int ans = 1;  
    for(int i = 2;i <= t;i++)  
    {  
        if(n % i == 0)  
        {  
            ans *= (i-1);  
            n /= i;  
            while(n % i == 0)  
            {  
                ans *= i;  
                n /= i;  
            }  
        }  
        if(n == 1) break;  
    }  
    if(n > 1) ans *= (n-1);  
    return ans;  
}  

逆元

1. 扩展欧几里德求解

ab≡1(modp) ⇒ ab−1≡0(modp) ⇒ ab+pt=1 故可以用扩展欧几里德求解

#include <iostream>
#include <cstdio>
using namespace std;
long long x,y,a,b,ans;
long long gcd(long long a,long long b)
{
    long long t,ret;
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ret=gcd(b,a%b);
    t=y;
    y=x-(a/b)*y;
    x=t;
    return ret;
}
int main()
{
    cin>>a>>b;
    ans=gcd(a,b);
    while(x>b)x-=b;
    while(x<0)x+=b;
    cout<<x<<endl;
    return 0;
}
2. 递推
for(int i=1;i<=maxn;i++)
inv[i]=(long long)(p-(p/i))*inv[p%i]%p;

Catalan数

原理理解:(两个应用模板)
括号化
矩阵连乘: P=a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?(h(n-1)种)
出栈次序
一个栈(无穷大)的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?
详情请参照百度百科

int main() // 求第n个卡特兰数
{
    cin>>n;
    h[0]=1;h[1]=1;
    for(int i=2;i<=n;i++)
        for(int j=0;j<=i-1;j++)
            h[i]=h[i]+h[j]*h[i-j-1];
    cout<<h[n];
    return 0;
}

高精

读入、储存与输出
以下代码块均包含以下语句 :

int s[255];//255位的数
//读入与储存 :
void read()
{
    char ss[255];

    scanf("%s",ss);
    for(int i=1;i<=strlen(ss);i++)
        s[i]=ss[strlen(ss)-i+1]-'0';
    s[0]=strlen(ss);//存长度
}
//输出 :
void print()
{
    for(int i=s[0];i>=1;i++)
        cout<<s[i];
}
高精度加法
高精加单精
void add(int *s,int x)//s存高精度数,x是要加的单精度
{
    int k=1;
    s[k]+=x;
    while(s[k]>=10)
    {
        s[k+1]+=s[k]/10;
        s[k++]%=10;
    }
    s[0]=k;
}
高精加高精
void add(int *s1,int *s2,int *ans)
{
    int len;
    len=max(s1[0],s2[0]);
    for(int i=1;i<=len;i++)
    {
        ans[i]+=s1[i]+s2[i];
        if(ans[i]>=10)
        {
            ans[i+1]+=ans[i]/10;
            ans[i]%=10;
        }
    }
    if(ans[len+1]!=0)len++;
    ans[0]=len;
}
高精度乘法
高精乘单精
void multiply(int *s,int x)
{
    for(int i=1;i<=n;i++)
    {
        c[i]+=s[i]*x;
        c[i+1]+=(s[i]*b)/10;
        c[i]%=10;
    }
    c[0]=s[0]+1;
    while(c[c[0]]>=10)
    {
        c[c[0]+1]+=c[c[0]]/10;
        c[c[0]]%=10;
        c[0]++;
    }
    while(c[0]>1&&c[c[0]]==0)
    {
        c[0]--;
    }
}
高精乘高精
void multiply(int *s1,int *s2,int *ans)
{
    for(int i=1;i<=s1[0];i++)
        for(itn j=1;j<=s2[0];j++)
        {
            ans[i+j-1]+=s1[i]*s2[j];
            ans[i+j]+=ans[i+j-1]/10;
            ans[i+j-1]%=10;
        }
    ans[0]=s1[0]+s2[0]+1;
    while(ans[0]>1&&ans[ans[0]]==0)
    {
        ans[0]--;
    }
}
高精度除法

放弃吧!

压位
const int opt=100000000;

void multiply(int *num,int x)
{
    for(int i=1;i<=num[0];i++)num[i]*=x;
    for(int i=1;i<=num[0];i++)
    {
        if(num[i]>=opt)
        {
            num[i+1]+=num[i]/opt;
            num[i]%=opt;
        };
        while(num[num[0]]!=0)
        {
            num[0]++;
        };
    }
}

void print(int *a)
{
    for(int i=a[0];i>=1;i--)
    {
        if(i==a[0])
        {
            cout<<a[i];
        }else
        {
            if(a[i]<10000000)cout<<0;
            if(a[i]<1000000)cout<<0;
            if(a[i]<100000)cout<<0;
            if(a[i]<10000)cout<<0;
            if(a[i]<1000)cout<<0;
            if(a[i]<100)cout<<0;
            if(a[i]<10)cout<<0;
            cout<<a[i];
        };
    };
}

⚠️:使用压位的时候的读入不要读错
比如不要把99存到数组的两个位置里面,而应该是一个;

图论

最短路

单源最短路SPFA

以下代码中包括邻接表(前向星)存图

#include<iostream>
#include<cstdio>
#define maxm 500005
#define maxn 10005
#define INF 0x7fffffff
using namespace std;
int n,m,s;

struct Edge
{
    int next;
    int to;
    int w;
}e[maxm] ;
int q[maxm],h[maxn],ok[maxn],dis[maxn];
int p;
void add_edge(int from ,int to,int w)
{
    e[++p].next=h[from];
    e[p].to=to;
    e[p].w=w;
    h[from]=p;
}
int tail,head;
void SPFA()
{

    for(int i=1;i<=n;i++)
      dis[i]=INF;
    dis[s]=0;
    ok[s]=1;
    q[tail++]=s;
    while(head<tail)
    {
        int u=q[head++];
        ok[u]=0;
        for(int i=h[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(dis[u]+e[i].w<dis[v])
             {
                dis[v]=dis[u]+e[i].w;
                if(!ok[v])
                {
                    q[tail++]=v;
                    ok[v]=1;
                 }
             }
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++)
    {
        int from,to,w;
        scanf("%d%d%d",&from,&to,&w);
        add_edge(from,to,w);
    }
    SPFA();
    for(int i=1;i<=n;i++)
    {
        printf("%d ",dis[i]);
    } 
    return 0;
}

双端队列优化版

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdlib>
#include<cstring>
using namespace std;
struct qq{
    int t,w;
}c;
int s,n,m,u;
int dis[10005];
vector<qq>e[10005];
bool vis[10005]; deque<int>q;
void spfa(){
    for(int i=1;i<=n;i++)dis[i]=2147483647;
    dis[s]=0;q.clear(); q.push_back(s);
    while(!q.empty()){
        u=q.front();q.pop_front();vis[u]=0;
        for(int i=0;i<e[u].size();i++){
            c=e[u][i];
            if(dis[c.t]>dis[u]+c.w){
                dis[c.t]=dis[u]+c.w;
                if(!vis[c.t]){
                    vis[c.t]=1;
                    if(q.empty()||dis[q.front()]<dis[c.t])q.push_back(c.t);
                    else q.push_front(c.t);
                }
            }
        }
    }
    for(int i=1;i<=n;i++)printf("%d ",dis[i]);
}
int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&u,&c.t,&c.w);
        e[u].push_back(c);
    }
    spfa();
    return 0;
}

copy by lijialin

多源最短路FLOYD
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 10000100
using namespace std;
int a[101],d[105][105];
int arrive[10005];
int main()
{
    int n,m,ans=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      scanf("%d",&d[i][j]); 
     for(int k=1;k<=n;k++)
      for(int i=1;i<=n;i++)
       for(int j=1;j<=n;j++)
       {
           d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
       }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        printf("%d\n",d[u][v]);
    }
    return 0;
} 

次短路

代码比较麻烦,不写了,说一下思路吧。
先用SPFA跑一遍,找出来最短路。把最短路记下来。
接下来,每次删掉最短路上的一条边,再跑一边SPFA。
运行一遍以后,路径长度最小的即次短路。

最小生成树(MST)

最小生成树即一个无向连通不含圈图中,连接G中所有点,且边集是E的子集,且边权和最小的生成树。(我解释的有点拗口)
最小生成树算法一共有两个:Prim和Kruskal算法,由于经并查集优化的Kruskal算法比Prim算法优秀得多,且Prim算法较容易理解,这里只给出Kruscal算法的模板。

Kruskal

下面展现两种做法,一种是普通的暴力枚举做法,另一种是并查集优化过的。并查集优化过的算法比较快,但是要忽略生成树的形状。就是说如果你需要用到新生成树的形状,那么不能使用此种方法。

1.普通方法:类似Prim算法
struct node{int u,v,w;}e[maxe];//u是起点,v是终点,w是权
node MST[maxe];
bool com(node a,node b){return a.w<b.w;};

void Kruskal()
{
    sort(e+1,e+m+1,com);//按边权从小到大排序
    for(int i=1;i<=m;i++)
    {
        if(!b[e[i].u]&&!b[e[i].v])//b判断是否已经在集合里
            MST[++tot]=e[i];
    };
}

以上版本是自己写的,感觉不对,于是抄下来了粉书上的伪代码和讲解:
把所有边排序,记第i小的边为e[i](1<=i<m)
初始化MST为空
初始化连通分量,让每个点自成一个独立的连通分量

for(int i=1;i<=m;i++)
    if(e[i].u和e[i].v不在同一个连通分量)
    {
        把边e[i]加入MST
        合并e[i].u和e[i].v所在的连通分量
    }   

在上面的伪代码中,最关键的地方在于”连通分量的查询与合并”:需要知道任意两个点是否在同一个连通分量中,还需要合并两个连通分量。
最容易想到的方法是”暴力”——每次”合并”时只在MST中加入一条边(如果使用邻接矩阵,只需G[e[i].u][e[i].v]=1),而”查询”时直接在MST中进行图遍历(DFS和BFS都可以判断连通性)。

2.使用并查集优化
int find(int x){return p[x]==x?x:p[x]=find(p[x]);}//并查集的find和路径压缩
int Kruskal()//返回的最小生成树的边权和
{
    int ans=0;
    for(int i=1;i<=n;i++)p[i]=i;//初始化并查集
    sort(edge+1,edge+m+1,com);//给边从小到大排序
    for(int i=1;i<=m;i++)
    {
        int x=find(edge[i].u);
        int y=find(edge[i].v);

        if(x!=y)
        {
            ans+=edge[i].w;//求和
            p[x]=y;
        };//如果在不同的集合,合并
    }
    return ans;
}

其实此处还有一个优化,虽然不会节省很长时间,但是,优势都是一点点积累出来的!就是循环枚举边的时候不用for而用while,当当前得到的最小生成树一共有n-1条边时,最小生成树就已经生成完了,剩下的边就不用再枚举了。

TARJIAN

tarjian

图的遍历

还有一点相关的东西就是传递闭包(Transitive Closure)
即在有向图中,有时不必关心路径长度,而只关心每两点间是否有通路,则可以用1和0分别表示”连通”和”不连通”。得到的结果称为有向图的传递闭包。
只需将FLOYD程序中的
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
改为
d[i][j]=d[i][j]||(d[i][k]&&d[k][j]));

建树

具体思路:对于一个节点来说,其他的任意一个节点,不是他的父节点,就是他的子节点。
传递闭包
详见上面图论部分Floyd算法。

LCA

#include<bits/stdc++.h>
#define MN 500005
using namespace std;
int m,n,s,tot,dep[MN],up[MN][20];

struct lll
 {
    int to;
    lll *ne;
 }a[2*MN];

lll *head[MN];

void DFS(int u)
{

    for(int i=1;(1<<i)<=n;i++)
     {
       up[u][i]=up[up[u][i-1]][i-1];
     }

    for(lll *e=head[u];e;e=e->ne)
     {
        if(dep[e->to]==-1)
         {
            dep[e->to]=dep[u]+1;
            up[e->to][0]=u;
            DFS(e->to);
         }
     }  

     return;
}

int LCA(int u,int v)
{
    if(dep[v]>dep[u]) swap(u,v);

    for(int i=19;i>=0;i--)
    {
        if(dep[up[u][i]]>=dep[v])
         u=up[u][i];
    }

    if(u==v) return v;

    for(int i=19;i>=0;i--)
    {
        if(up[u][i]!=up[v][i])
        {
         u=up[u][i];
         v=up[v][i];
        } 
    }

    return up[u][0];
}

int main()
{
   scanf("%d%d%d",&n,&m,&s);

   for(int i=1;i<n;i++)
    {
      int x,y;
      scanf("%d%d",&x,&y);
      a[++tot].ne=head[x];
      head[x]=&a[tot];
      a[tot].to=y;
      a[++tot].ne=head[y];
      head[y]=&a[tot];
      a[tot].to=x;
    }

   memset(dep,-1,sizeof(dep));
   dep[s]=0;
   up[s][0]=0;
   DFS(s);

   for(int j=1;j<=m;j++)
    {
       int u,v;
       scanf("%d%d",&u,&v);
       printf("%d\n",LCA(u,v));
    }

}

copy by RWZ

线段树

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
struct cs
{
    LL ll,rr,vv;
}T[824290];
LL a[200005],v[824290];
LL n,m,x,y,z,sum,N;
void clean(LL x)
{
    if(!v[x])return;
    T[x].vv+=(T[x].rr-T[x].ll+1)*v[x];
    if(T[x].ll!=T[x].rr)
    {
        v[x*2]+=v[x];
        v[x*2+1]+=v[x];
    }
    v[x]=0;
}
{
    T[num].ll=x;
    T[num].rr=y;
    if(x==y) T[num].vv=a[x],return;
    maketree(x,(x+y)/2,num*2);
    maketree((x+y)/2+1,y,num*2+1);
    T[num].vv=T[num*2].vv+T[num*2+1].vv;
}
void inc(LL x,LL y,LL z,LL num)
 {
    clean(num);
    if(x<=T[num].ll&&T[num].rr<=y) v[num]+=z, return;
    T[num].vv+=(min(y,T[num].rr)-max(x,T[num].ll)+1)*z;
    if(T[num].ll==T[num].rr) return;
    int mid=(T[num].ll+T[num].rr)/2;
    if(x>mid)inc(x,y,z,num*2+1);
    else
      if(y<=mid)inc(x,y,z,num*2);else
        {
         inc(x,y,z,num*2);
         inc(x,y,z,num*2+1);
        }
}
void out(int LL,int LL,LL num)
{
    clean(num);
    if(x<=T[num].ll&&T[num].rr<=y) sum+=T[num].vv,return;
    int mid=(T[num].ll+T[num].rr)/2;
    if(x>mid)out(x,y,num*2+1);
      else
      if(y<=mid)out(x,y,num*2);
        else
        {
         out(x,y,num*2);
         out(x,y,num*2+1);
        }
}
int main()
{
    scanf("%d%d",&n,&N);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    maketree(1,n,1);
    for(int i=1;i<=N;i++)
    {
        scanf("%d%d%d",&m,&x,&y);
        if(m==1)
        {
            scanf("%d",&z);
            inc(x,y,z,1);
        }
        else
        {
            sum=0;
            out(x,y,1);
            printf("%lld\n",sum);//用cout输出什么事都没有 
        }
    }
}

树状数组

比线段树短啊

1. 单点修改,区间查询(codevs1080 线段树练习)

单点修改:

#define lowbit(x) x&(~x+1)
using namespace std;
int a[MAXN],tree[MAXN],n,q;
void add(int k,int x)
{
    while(k <= n)
    {
        tree[k] += x;
        k += lowbit(k);
    }
}

  区间查询:
  

int read(int k)
{
    int sum = 0;
    while(k)
    {
        sum += tree[k];
        k -= lowbit(k);
    }
    return sum;
}
/*
printf("%d\n",read(b) - read(a-1));//输出
2.区间修改,单点查询(codevs1081 线段树练习 2)

修改和查询函数与上面是一样的,不过预处理的时候有所不同,这里只放主函数代码

int main()
{
    scanf("%d",&n);
    for(int i = 1; i <= n; i ++)
    {
        scanf("%d",&a[i]);
        add(i,a[i]);
        add(i+1,-a[i]);
    }
    scanf("%d",&q);
    int t;
    for(int i = 1; i <= q; i ++)
    {
        scanf("%d",&t);
        if(t == 1)
        {
            int a,b,x;
            scanf("%d%d%d",&a,&b,&x);
            add(a,x);
            add(b+1,-x);

        }
        if(t == 2)
        {
            int a;
            scanf("%d",&a);
            printf("%d\n",read(a));
        }
    }
    return 0;
}
3. 区间修改,区间查询(codevs1082 线段树练习 3)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#define MAXN 200000+5
#define lowbit(x) x&(~x+1)
using namespace std;
typedef long long ll;
ll a[MAXN],d1[MAXN],d2[MAXN],n,q;
void add(ll *d,ll k,ll x)
{
    while(k <= n)
    {
        d[k] += x;
        k += lowbit(k);
    }
}
ll read(ll *d,ll k)
{
    ll sum = 0;
    while(k)
    {
        sum += d[k];
        k -= lowbit(k);
    }
    return sum;
}
int main()
{
    scanf("%lld",&n);
    for(int i = 1; i <= n; i ++)
    {
        scanf("%lld",&a[i]);
        add(d1,i,a[i]);
        add(d1,i+1,-a[i]);
        add(d2,i,i*a[i]);
        add(d2,i+1,-a[i]*(i+1));
    }
    scanf("%lld",&q);
    int t;
    for(int i = 1; i <= q; i ++)
    {
        scanf("%d",&t);
        if(t == 1)
        {
            int a,b,x;
            scanf("%d%d%d",&a,&b,&x);
            add(d1,a,x);
            add(d1,b+1,-x);
            add(d2,a,x*a);
            add(d2,b+1,-x*(b+1));
        }
        if(t == 2)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            printf("%lld\n",((b+1)*read(d1,b)-read(d2,b))-(a*read(d1,a-1)-read(d2,a-1)));
        }
    }
    return 0;
}

并查集

#include<iostream>
#include<cstdio>
using namespace std;
int x,y,n,m,q;
int fa[20020];
int find (int x)
{
    if(fa[x]!=x) fa[x]=find(fa[x]);
    return fa[x];
}
void unionn(int x,int y)
{
   int r1=find(x),r2=find(y);
   if(r1!=r2) fa[r2]=r1;    
} 
int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        unionn(x,y);
    }
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&x,&y);
        if(find(x)==find(y)) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

字符串

KMP

KMP1 KMP2 KMP3

TRIE树

TRIE1 TRIE2

MANACHER

#include<iostream>
#include<cmath>
#include<cstring>
#define maxn 51000100
using namespace std;
int n,hw[maxn],ans;
char a[maxn],s[maxn<<2];//因为要插入字符所以*2,空间限制也很安全
void manacher()
{
    int maxright=0,mid;
    for(int i=1;i<n;i++)
    {
        if(i<maxright)
            hw[i]=min(hw[2*mid-i],hw[mid]+mid-i);//这句话重点,好好理解
        else
            hw[i]=1;
        for(;s[i+hw[i]]==s[i-hw[i]];++hw[i]);//继续扩展
        if(hw[i]+i>maxright)
        {
            maxright=hw[i]+i;//更新
            mid=i;
        }
    }
}
void change()
{
    s[0]=s[1]='#';
    for(int i=0;i<n;i++)
    {
        s[i*2+2]=a[i];
        s[i*2+3]='#';
    }
    n=n*2+2;
    s[n]=0;
}
int main()
{
    scanf("%s",a);
    n=strlen(a);
    change();
    manacher();
    ans=1;
    for(int i=0;i<n;i++)
        ans=max(ans,hw[i]);
    printf("%d",ans-1);//想想为什么要-1
    return 0; 
}

动态规划

钟长者说:有几个未知量,DP数组就有几维,若求个数能再省掉最后一维。
然而这只是一般情况,例如有个例外:HAOI2012 音量调节/Luogu P1877,这道题就不能省掉最后一维。
铭哥说:重推所有的DP方程是复习DP的最佳方法

01背包

for(int i=1;i<=m;i++)//m个物品
    for(int j=1;j<=w;j++)//背包容量为w
        if(a[i]<=j)
        {
            dp[i][j]=max(dp[i-1,j-a[i]]+val[i],dp[i-1,j]);
            //a数组是占用的容量,val是价值
        }else
        {
            dp[i][j]=dp[i-1][j];
        };
//此时dp[m][w]即为最大权值

优化:
//条件和上面一样
for(int i=1;i<=n;i++)
    for(int j=w;i>=a[i];j--)
        dp[j]=(dp[j],dp[j-a[i]]+val[i]);

完全背包

//条件和上面一样,只是每个物品可以取无数次
for(int i=1;i<=n;i++)
    for(int j=a[i];i<=w;j++)//注意这里的改动
        dp[j]=(dp[j],dp[j-a[i]]+val[i]);

分组背包(多维背包)(多重限制背包(迷))

即有好几种背包的条件,分别写dp满足条件就可以了

其他模板

归并排序(逆序对)

#include<iostream>
#include<cstdio>
#define MAXN 100005
using namespace std;

int a[MAXN],tmp[MAXN];
int ans;
void mergesort(int l,int r)
{
    if(l==r) return;

    int mid=(l+r)/2;
    mergesort(l,mid);
    mergesort(mid+1,r);
    int i=l,j=mid+1,pos=l;
    while (i<=mid&&j<=r)
    {
        if(a[i]<=a[j])
        {  
            tmp[pos++]=a[i++];
        }
        else 
        {
            tmp[pos++]=a[j++];
            ans+=(mid-i+1);
        }

    }

    while (i<=mid)tmp[pos++]=a[i++];
    while (j<=r)tmp[pos++]=a[j++];
    for(int i=l;i<=r;i++)
    {
        a[i]=tmp[i];
    }
  return ;
} 
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    mergesort(1,n);
    cout<<ans;
    return 0;
}

二分

void binary(int l,int r)    //找最小
{
    while(r>l)
    {
        mid=(l+r)/2;
        if(check(mid))r=mid;
            else l=mid+1;
    }
    ans=l;
}

void binary(int l,int r)    //找最大
{
    while(r>l)
    {
        mid=(r+l)/2+1;  //特别注意
        if(check(mid))l=mid+1;
            else r=mid;
    }
    ans=l;
}

网络流(dinic)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x7fffffff
#define MAXN 10005
#define MAXM 100005
using namespace std;
struct EDGE
{
    int next,to,w;
}e[MAXM<<1];
int p;
int q[MAXM+MAXN],h[MAXM];
int deep[MAXN];
inline void add_edge(int from,int to,int w)
{
    e[++p].next=h[from];
    e[p].to=to;
    e[p].w=w;
    h[from]=p;
}
int s,t;
inline bool bfs()
{
    memset(deep,0,sizeof(deep));
    int head=0,tail=0;
    q[++tail]=s;
    deep[s]=1;
    while(head<tail)
    {
        int now=q[++head];
        for(int i=h[now];i;i=e[i].next)
        {
            int v=e[i].to;
            if(!deep[v]&&e[i].w)
            {
                deep[v]=deep[now]+1;
                q[++tail]=v;
                if(v==t) return true;
            }
        }
    }
    return false;
}
inline int dfs(int now,int cur_flow)
{
    if(now==t) return cur_flow;
    int rest=cur_flow;
    for(int i=h[now];i;i=e[i].next)
    {
        int v=e[i].to;
        if(deep[v]==deep[now]+1&&rest&&e[i].w)
        {
            int new_flow=dfs(v,min(rest,e[i].w));
            rest-=new_flow;
            e[i].w-=new_flow;
            (i%2)?(e[i+1].w+=new_flow):(e[i-1].w+=new_flow);
        }
    }
    return cur_flow-rest;
}
inline int dinic()
{
    int ans=0;
    while(bfs())
    {
        ans+=dfs(s,inf);
    }
    return ans;
}
int main()
{
    int n,m;
    cin>>n>>m>>s>>t;
    int from,to,w;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&from,&to,&w);
        add_edge(from,to,w);
        add_edge(to,from,0);
    }
    cout<<dinic();
    return 0;
}

最长上升子序列

把楼下离散化去掉即可

最长公共子序列

离散化+最长上升子序列

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
int a1[100010],a2[100010];
int belong[100010];
int f[100010],b[100010],len;
int main()
{
    freopen("a.in","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a1[i]);
        belong[a1[i]]=i;
    }
    for(int i=1;i<=n;i++)
    scanf("%d",&a2[i]);
    for(int i=1;i<=n;i++)
    {
        if(belong[a2[i]]>b[len])
        {
            b[++len]=belong[a2[i]];
            f[i]=len;
            continue;
        }
        int k=lower_bound(b+1,b+len+1,belong[a2[i]])-b;
        b[k]=belong[a2[i]];
        f[i]=k;
    }
    printf("%d\n",len);
    return 0;
}

RMQ(区间最值)

#include<iostream>
#include<cstdio>
#define MAXN 100005
using namespace std;
int n,m;
long long a[MAXN],d[MAXN][18];
inline int read ()
{
    int n=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') 
    {
        n=n*10+ch-'0';
        ch=getchar();
    }
    return n;
}
inline void RMQ_pre()
{
    for(int i=1;i<=n;i++)
    {
        d[i][0]=read();
    }
    for(int j=1;(1<<j)<=n;j++)//(1<<j)<=n
     for(int i=1;i+(1<<j)-1<=n;i++)//i+(1<<j)-1<=n
      d[i][j]=max(d[i][j-1],d[i+(1<<(j-1))][j-1]);
} 

inline int RMQ(int l,int r)
{
    int k=0;
    while((1<<k)<=r-l+1) k++;
    k--;
    return max(d[l][k],d[r-(1<<k)+1][k]);
}

int main()
{
  n=read();
  m=read();
  RMQ_pre();
  for(int i=1;i<=m;i++)
  {
    int l=read(),r=read();
    printf("%d\n",RMQ(l,r));
  }
  return 0;
}

TREAP

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
插入x数
删除x数(若有多个相同的数,因只删除一个)
查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
查询排名为x的数
求x的前驱(前驱定义为小于x,且最大的数)
求x的后继(后继定义为大于x,且最小的数)

#include <cstdio> 
#include <cstdlib>
#include <ctime>
#define N 500005
using namespace std;
int inline read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int ch[N][2],val[N],pri[N],siz[N],sz;
void update(int x){siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]];}
int new_node(int v)
{
    siz[++sz]=1;
    val[sz]=v;
    pri[sz]=rand();
    return sz;
}
int merge(int x,int y)
{
    if (!x || !y) return x+y;
    if (pri[x]<pri[y])
    {
        ch[x][1]=merge(ch[x][1],y);
        update(x);
        return x;
    }
    else
    {
        ch[y][0]=merge(x,ch[y][0]);
        update(y);
        return y;
    }
}
void split(int now,int k,int &x,int &y)
{
    if (!now) x=y=0;
    else
    {
        if (val[now]<=k)
            x=now,split(ch[now][1],k,ch[now][1],y);
        else
            y=now,split(ch[now][0],k,x,ch[now][0]);
        update(now);
    }
}
int kth(int now,int k)
{
    while(1)
    {
        if (k<=siz[ch[now][0]])
            now=ch[now][0];
        else
        if (k==siz[ch[now][0]]+1)
            return now;
        else
            k-=siz[ch[now][0]]+1,now=ch[now][1];
    }
}
int main()
{
    //srand((unsigned)time(NULL));
    int T,com,x,y,z,a,b,root=0;
    scanf("%d",&T);
    while(T--)
    {
        com=read(),a=read();
        if (com==1)
        {
            split(root,a,x,y);
            root=merge(merge(x,new_node(a)),y);
        }
        else
        if (com==2)
        {
            split(root,a,x,z);
            split(x,a-1,x,y);
            y=merge(ch[y][0],ch[y][1]);
            root=merge(merge(x,y),z);
        }
        else
        if (com==3)
        {
            split(root,a-1,x,y);
            printf("%d\n",siz[x]+1);
            root=merge(x,y);
        }
        else
        if (com==4)
            printf("%d\n",val[kth(root,a)]);
        else
        if (com==5)
        {
            split(root,a-1,x,y);
            printf("%d\n",val[kth(x,siz[x])]);
            root=merge(x,y);
        }
        else
        {
            split(root,a,x,y);
            printf("%d\n",val[kth(y,1)]);
            root=merge(x,y);
        }
    }
    return 0;
}

匈牙利算法

bool dfs(int now)
{
  for (int a=1;a<=m;a++)
  if (match[now][a] && !use[a]) 
  {
    use[a]=true;
    if (!result[a] || dfs(result[a])) 
    {
     result[a]=now;
     return true;
    } 
  }
  return false;
}

void xiongyali() 
{
  int ans=0;
  for (int a=1;a<=n;a++)
  {
     memset(use,false,sizeof(use));
     if (dfs(a)) ans++;
  }
}

对于变式题
ans
n-ans
m-ans
n+m-ans
总有一个是答案

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值