冲刺NOIp2016算法模板

来源:互联网 发布:瑜伽初学者软件 编辑:程序博客网 时间:2024/05/21 09:30

    • 数据结构
      • 队列
      • 树状数组
        • 应用模型树状数组求逆序对
    • STL
      • 不定长度数组
      • Queue
      • Deque
      • Bitset
      • Set
      • Multiset
      • Map
      • Algorithm里其他好用的函数
        • Next_permutation
        • Lower_bound与Upper_bound
        • Merge
        • sort起始地址结束地址compare函数
        • Reverse
        • Unique
    • 数论
      • 快速幂
      • 矩阵快速幂
      • 筛法求素数 欧拉筛法
      • 最大公约数和最小公倍数
      • 扩展欧几里德
      • 欧拉函数PHI
      • 求n的欧拉函数值
      • 逆元
        • 扩展欧几里德求解
        • 递推
      • Catalan数
      • 高精
        • 高精度加法
          • 高精加单精
          • 高精加高精
        • 高精度乘法
          • 高精乘单精
          • 高精乘高精
        • 高精度除法
        • 压位
    • 图论
      • 最短路
        • 单源最短路SPFA
        • 多源最短路FLOYD
      • 次短路
      • 最小生成树MST
      • Kruskal
        • 普通方法类似Prim算法
        • 使用并查集优化
      • TARJIAN
      • 图的遍历
      • 建树
      • LCA
      • 线段树
      • 树状数组
        • 单点修改区间查询codevs1080 线段树练习
        • 区间修改单点查询codevs1081 线段树练习 2
        • 区间修改区间查询codevs1082 线段树练习 3
      • 并查集
    • 字符串
      • KMP
      • TRIE树
      • MANACHER
    • 动态规划
      • 01背包
      • 完全背包
      • 分组背包多维背包多重限制背包迷
    • 其他模板
      • 归并排序逆序对
      • 二分
      • 网络流dinic
      • 最长上升子序列
      • 最长公共子序列
      • RMQ区间最值
      • TREAP
      • 匈牙利算法

数据结构

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);Stackstack<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相当于endthird.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);sortbool 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(起始地址,结束地址,去重条件函数);//按照函数里面编写的规则去重,当然也可以没有第三个参数

数论

快速幂

计算 basenmod 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 0x7fffffffusing 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 10000100using 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 500005using 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 longusing 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 51000100using 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 100005using 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 100005using 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 100005using 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 500005using 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
总有一个是答案

原创粉丝点击