noip模板整理

来源:互联网 发布:apache phoenix 编辑:程序博客网 时间:2024/05/22 11:54

距离noip还有⑨-1天,差不多要开始撸模板了,在这里整理下noip各式各样算法的模板。

图论 :

(论图╮(╯▽╰)╭)

spfa:图上乱搞必备,并非只止步于求最短路 | 最长路,spfa可是图上dp!!
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int sz = 200100;deque < int > q; // slfint n,m;int head[sz],nxt[sz],dist[sz];struct gtnd{    int t,d;}l[sz];int tot = 1;bool use[sz];int pre[sz];//记录路径int tim[sz];//判负环 void build(int f,int t,int d){    l[tot].t = t;    l[tot].d = d;    nxt[tot] = head[f];    head[f] = tot ++;}int spfa(int s,int e){    for(int i = 1 ; i <= n ; i ++)        dist[i] = 2147483641;    dist[s] = 0;    use[s] = 1;    q.push_front(s);    while(!q.empty())    {        int f = q.front();        q.pop_front();        use[f] = 0;        for(int i = head[f] ; i ; i = nxt[i])        {            int t = l[i].t;            if(dist[t] > dist[f] + l[i].d)            {                dist[t] = dist[f] + l[i].d;                tim[t] = tim[f] + 1;                if(tim[t] > n)                    return -1;                pre[t] = f;                if(!use[t])                {                    use[t] = 1;                    if(!q.empty())                    {                        if(dist[t] < dist[q.front()])                            q.push_front(t);                        else                            q.push_back(t);                    }                    else                        q.push_back(t);                }            }        }    }    return dist[e];}void print_path(int u) // 打印路径 {    printf("%d ",u);    if(pre[u])        print_path(pre[u]);}int main(){    return 0;}

Kruskal,最小生成树,好像可以跟01分数规划搞一搞?
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int sz = 200010;int f[sz];int n,m;int find(int x){    if(f[x] == x)        return x;    return f[x] = find(f[x]);}struct gtnd{    int f,t,d;}l[sz];bool cmp(gtnd swc,gtnd zcw){    return swc.d > zcw.d; }int Kruskal(){    int ans = 0;    for(int i = 1 ; i <= n ; i ++)        f[i] = i;    sort(l+1,l+m+1,cmp);    for(int i = 1 ; i <= m ; i ++)    {        int u = l[i].f , v = l[i].t;        int fu = find(u) , fv = find(v);        if(fu != fv)        {            f[fu] = fv;            ans += l[i].d;        }    }    return ans;}int main(){    return 0;}

拓扑排序,图上dp有时会用到╮(╯▽╰)╭。
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int sz = 200010;int head[sz],nxt[sz],l[sz];int ru[sz];int tot = 1;int n,m;queue < int > q;void build(int f,int t){    l[tot] = t;    nxt[tot] = head[f];    head[f] = tot ++;}void top_sort(){    for(int i = 1 ; i <= n ; i ++)        if(!ru[i])        {            q.push(i);            printf("%d ",i);        }    while(!q.empty())    {        int f = q.front();        q.pop();        for(int i = head[f] ; i ; i = nxt[i])        {            ru[l[i]] --;            if(!ru[l[i]])            {                printf("%d ",l[i]);                q.push(l[i]);            }        }    }}int main(){    scanf("%d%d",&n,&m);    for(int i = 1 ; i <= m ; i ++)    {        int f,t;        scanf("%d%d",&f,&t);        ru[t] ++;        build(f,t);    }    top_sort();    return 0;}

树上最近公共祖先,lca,这里推荐倍增版,滋磁在树上快速搞事。
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int size = 200010;int head[size],next[size],dist[size][32];int par[size][32],deep[size];int tot = 1;struct dc{    int t,d;}l[size];void build(int f,int t,int d){    l[tot].t = t;    l[tot].d = d;    next[tot] = head[f];    head[f] = tot ++;}int n;void dfs(int u,int fa,int dep,int dis){    par[u][0] = fa;    dist[u][0] = dis;    deep[u] = dep;    for(int i = head[u] ; i ; i = next[i])    {        int v = l[i].t;        if(v != fa)            dfs(v,u,dep+1,l[i].d);    }}int lca(int u,int v){    int ans = 0;    if(deep[u] < deep[v])        swap(u,v);    for(int i = 31 ; i >= 0 ; i --)        if(deep[par[u][i]] >= deep[v])            ans += dist[u][i] , u = par[u][i];    for(int i = 31 ; i >= 0 ; i --)        if(par[u][i] != par[v][i])            ans += dist[u][i] + dist[v][i] , u = par[u][i] , v = par[v][i];    if(u != v)        ans += dist[u][0] + dist[v][0] , u = par[u][0] , v = par[v][0];    return ans;}int main(){    scanf("%d",&n);    for(int i = 1 ; i < n ; i ++)    {        int f,t,d;        scanf("%d%d%d",&f,&t,&d);        f ++ , t ++;        build(f,t,d);        build(t,f,d);    }    dfs(1,0,1,0);    for(int i = 1 ; i <= 31 ; i ++)        for(int j = 1 ; j <= n ; j ++)            par[j][i] = par[par[j][i-1]][i-1] , dist[j][i] = dist[j][i-1] + dist[par[j][i-1]][i-1];    int m;    scanf("%d",&m);    for(int i = 1 ; i <= m ; i ++)    {        int u,v;        scanf("%d%d",&u,&v);        printf("%d\n",lca(u+1,v+1));    }    return 0;}

tarjan系列
这里写图片描述
tarjan 求 scc , 有向图大腿。

#include<iostream>#include<cstdio>#include<cstring>#include<stack>#include<algorithm>using namespace std;const int sz = 200010;int head[sz],nxt[sz],dist[sz],l[sz];int low[sz],dfn[sz],scc_num,dfs_clock;stack < int > s;struct gtnd{    int p,num;    bool operator <(const gtnd &a)const    {        return num < a.num;    }}scc[sz];int tarjan(int u){    dfn[u] = low[u] = dfs_clock ++;    s.push(u);    for(int i = head[u] ; i ; i = nxt[i])    {        int v = l[i];        if(!dfn[v])        {            low[v] = tarjan(v);            low[u] = min(low[u],low[v]);        }        else if(!scc[v].num)            low[u] = min(low[u],dfn[v]);    }    if(low[u] == dfn[u])    {        scc_num ++;        while(12 < 450)        {            if(s.empty())                break;            int v = s.top();            s.pop();            scc[v].num = scc_num;            scc[v].p = v;            if(u == v)                break;        }    }    return low[u];}int main(){    return 0;}

tarjan求割点,然而基本没用过,忘得差不多了。

#include<iostream>#include<cstdio>#include<cstring>#include<stack>#include<algorithm>using namespace std;const int sz = 200010;int head[sz],nxt[sz],l[sz];int tot = 1;int n,m;void build(int f,int t){    l[tot] = t;    nxt[tot] = head[f];    head[f] = tot ++;}int low[sz],dfn[sz],dfs_clock;bool is_cut[sz];int tarjan(int u,int fa){    dfn[u] = low[u] = ++ dfs_clock;    int child = 0;    for(int i = head[u] ; i ; i = nxt[i])    {        int v = l[i];        if(!dfn[v])        {            child ++;            low[v] = tarjan(v,u);            low[u] = min(low[u],low[v]);            if(dfn[u] <= low[v])                is_cut[u] = 1;        }        else if(dfn[v] < dfn[u] && v != fa)            low[u] = min(dfn[v],low[u]);    }    if(child == 1 && fa == 0)        is_cut[u] = 0;    return low[u];}int main(){    scanf("%d%d",&n,&m);    for(int i = 1 ; i <= m ; i ++)    {        int f,t;        scanf("%d%d",&f,&t);        build(f,t);        build(t,f);    }    for(int i = 1 ; i <= n ; i ++)        if(!dfn[i])            tarjan(i,0);    int ans = 0;    for(int i = 1 ; i <= n ; i ++)        if(is_cut[i])            ans ++;    printf("%d\n",ans);    for(int i = 1 ; i <= n ; i ++)        if(is_cut[i])            printf("%d ",i);    return 0;}

tarjan求桥,就是割点改个等于号。

int low[sz],dfn[sz],dfs_clock;struct gtnd{    int f,t;}cut[sz];int conut;int tarjan(int u,int fa){    dfn[u] = low[u] = ++ dfs_clock;    int child = 0;    for(int i = head[u] ; i ; i = nxt[i])    {        int v = l[i];        if(!dfn[v])        {            child ++;            low[v] = tarjan(v,u);            low[u] = min(low[u],low[v]);            if(dfn[u] < low[v])                cut[++conut].f = u , cut[count].t = v;        }        else if(dfn[v] < dfn[u] && v != fa)            low[u] = min(dfn[v],low[u]);    }    return low[u];}

树的直径,滋磁树上乱搞,这里以codevs1814为例。
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int n;const int size = 200010;int head[size],next[size],l[size];int tot = 1;void build(int f,int t){    l[tot] = t;    next[tot] = head[f];    head[f] = tot ++;}int pos,dist;void dfs(int u,int p,int dis){    if(dis > dist)        dist = dis , pos = u;    for(int i = head[u] ; i ; i = next[i])    {        int v = l[i];        if(v != p)            dfs(v,u,dis+1);    }}int main(){    scanf("%d",&n);    for(int i = 1 ; i <= n ; i ++)    {        int ll,rr;        scanf("%d%d",&ll,&rr);        if(ll)            build(ll,i) , build(i,ll);        if(rr)            build(rr,i) , build(i,rr);    }    dfs(1,-1,0);    dist = 0;    dfs(pos,-1,0);    printf("%d\n",dist);    return 0;} 

floyd,n^3最短路,带有比较特殊的性质,可以有各种变形,但往往难度超出noip范围。
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define ll long longusing namespace std;const int sz = 2550;int dis[sz][sz];int n,m,s,e;int main(){    scanf("%d%d%d%d",&n,&m,&s,&e);    for(int i = 1 ; i <= n ; i ++)        for(int j = 1 ; j <= n ; j ++)            dis[i][j] = 214748364;    for(int i = 1 ; i <= n ; i ++)        dis[i][i] = 0;    for(int i = 1 ; i <= m ; i ++)    {        int f,t,d;        scanf("%d%d%d",&f,&t,&d);        dis[f][t] = min(dis[f][t],d);        dis[t][f] = min(dis[f][t],dis[t][f]);    }    for(int k = 1 ; k <= n ; k ++)        for(int i = 1 ; i <= n ; i ++)            for(int j = 1 ; j <= n ; j ++)                dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]);    printf("%d\n",dis[s][e]);    return 0;}

数论

暴力(划);

gcd & lcm ,noip唯有的几个可以加特技的算法。
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int gcd(int a,int b){    if(b == 0)        return a;    return gcd(b,a%b);}int lcm(int a,int b){    return a * b / gcd(a,b);}int main(){    int a,b;    while(scanf("%d%d",&a,&b))    {        printf("%d\n",gcd(a,b));    }    return 0;}

埃氏筛,筛素数速度上仅次于欧拉筛。
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;bool is_prime[2000100];int main(){    int n;    scanf("%d",&n);    is_prime[1] = 1;    for(int i = 2 ; i * i <= n ; i ++)    {        if(!is_prime[i])            for(int j = i * i ; j <= n ; j += i)                is_prime[j] = 1;    }    return 0;} 

快速幂,快速求一个数的次方,搞事必备
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int ksm(int x,int p){    if(p == 0)        return 1;    if(p == 1)        return x;    if(p == 2)        return x * x;    int temp = ksm(x,p/2);    if(p % 2 == 1)        return temp * temp * x;    if(p % 2 == 0)        return temp * temp;}int main(){    int x,p;    while(scanf("%d%d",&x,&p))        printf("%d\n",ksm(x,p));    return 0;}

逆元,只推荐费马小定理,要是noip题mod不是素数我就暴力!
费马小定理: 假如p是质数,且a,p互质,那么 a^(p-1)≡1(mod p)。
由此可得,a^(p-2) ≡ 1 / a (mod p),所以在mod p意义下,除以 a 等价于乘上 a^(p-2),即 a^(p-2) 为 a 的逆元。
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define ll long longusing namespace std;const int mod = 1000000007;ll ksm(ll x,ll p){    if(p == 0)        return 1;    if(p == 1)        return x % mod;    if(p == 2)        return ((x%mod) * (x%mod))%mod;    int temp = ksm(x,p/2) % mod;    if(p % 2 == 1)        return (((temp * temp) % mod) * (x%mod));    if(p % 2 == 0)        return (temp * temp) % mod;}int get(int a){    return ksm(a,mod-2);}int main(){    return 0;}

数据结构

单调队列,滋磁O(n)序列上搞事,多用于dp优化。

这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int sz = 2000100;deque < int > q;int num[sz];int n,k;int main(){    scanf("%d%d",&n,&k);    for(int i = 1 ; i <= n ; i ++)        scanf("%d",&num[i]);    for(int i = 1 ; i <= k ; i ++)    {        while(!q.empty() && num[q.back()] < num[i])            q.pop_back();        q.push_back(i);    }    printf("%d\n",num[q.front()]);    for(int i = k + 1 ; i <= n ; i ++)    {        while(!q.empty() && q.front() < i - k + 1)            q.pop_front();        while(!q.empty() && num[q.back()] < num[i])            q.pop_back();        q.push_back(i);        printf("%d\n",num[q.front()]);    }    return 0;}

set,用于logn找前驱后继或当map使233,这里以noi openjudge的冷血格斗场为例。
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<set>#include<map>#define ll long longusing namespace std;struct gtnd{    ll id;    ll p;    bool operator <(const gtnd &a)const    {        return p < a.p;    }};map < ll , ll > m;set < gtnd > s;set < gtnd > :: iterator f1,f2,temp;int n;int main(){    scanf("%d",&n);    gtnd sta;    sta.id = 1 , sta.p = 1000000000;    m[sta.p] = 1;    s.insert(sta);    for(int i = 1 ; i <= n ; i ++)    {        gtnd nxt;        scanf("%lld%lld",&nxt.id,&nxt.p);        printf("%lld ",nxt.id);        if(m[nxt.p])        {            printf("%lld\n",m[nxt.p]);            m[nxt.p] = min(m[nxt.p],nxt.id);            continue;        }        else            m[nxt.p] = nxt.id;        f1 = s.lower_bound(nxt);        f2 = f1;        f2 --;        ll id_f1 = m[(*f1).p] , id_f2 = m[(*f2).p];        ll a_f1 = abs((*f1).p - nxt.p) , a_f2 = abs((*f2).p - nxt.p);        if(f2 == s.end())            printf("%lld\n",id_f1);        else if(f1 == s.end())            printf("%lld\n",id_f2);        else if(a_f1 > a_f2)            printf("%lld\n",id_f2);        else if(a_f1 < a_f2)            printf("%lld\n",id_f1);        else        {            if(id_f1 < id_f2)                printf("%lld\n",id_f1);            else                printf("%lld\n",id_f2);        }        s.insert(nxt);    }    return 0;}

线段树,noip考不到但是可以水分的大腿,滋磁区间快速搞事。

这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define ll long longusing namespace std;const int sz = 200010;struct xd_tree{    int l,r;    ll sum,min,max,add;}tree[sz*4];int num[sz];int n;void updata(int p){    tree[p].min = min(tree[p<<1].min,tree[p<<1|1].min);    tree[p].max = max(tree[p<<1].max,tree[p<<1|1].max);    tree[p].sum = tree[p<<1].sum + tree[p<<1|1].sum;}void build_tree(int p,int l,int r){    tree[p].l = l , tree[p].r = r;    if(l == r)    {        tree[p].min = tree[p].max = tree[p].sum = num[l];        return ;    }    int mid = l + r >> 1;    build_tree(p<<1,l,mid);    build_tree(p<<1|1,mid+1,r);    updata(p);}void spread(int p){    if(tree[p].add)    {        tree[p<<1].sum += tree[p].add * (tree[p<<1].r - tree[p<<1].l + 1);        tree[p<<1|1].sum += tree[p].add * (tree[p<<1|1].r - tree[p<<1|1].l + 1);        tree[p<<1].min += tree[p].add;        tree[p<<1|1].min += tree[p].add;        tree[p<<1].max += tree[p].add;        tree[p<<1|1].max += tree[p].add;        tree[p<<1].add += tree[p].add;        tree[p<<1|1].add += tree[p].add;        tree[p].add = 0;    }    return ;}void change(int p,int l,int r,int x){    if(l <= tree[p].l && tree[p].r <= r)    {        tree[p].sum += x * (tree[p].r - tree[p].l + 1);        tree[p].max += x;        tree[p].min += x;        tree[p].add += x;        return ;    }    spread(p);    int mid = tree[p].l + tree[p].r >> 1;    if(l <= mid)        change(p<<1,l,r,x);    if(r > mid)        change(p<<1|1,l,r,x);    updata(p);}ll ask_sum(int p,int l,int r){    if(l <= tree[p].l && tree[p].r <= r)        return tree[p].sum;    spread(p);    int mid = tree[p].l + tree[p].r >> 1;    ll ans = 0;    if(l <= mid)        ans += ask_sum(p<<1,l,r);    if(r > mid)        ans += ask_sum(p<<1|1,l,r);    return ans; }ll ask_min(int p,int l,int r){    if(l <= tree[p].l && tree[p].r <= r)        return tree[p].min;    spread(p);    int mid = tree[p].l + tree[p].r >> 1;    ll ans = 214748364111111ll;    if(l <= mid)        ans = min(ask_min(p<<1,l,r),ans);    if(r > mid)        ans = min(ask_min(p<<1|1,l,r),ans);    return ans;}ll ask_max(int p,int l,int r){    if(l <= tree[p].l && tree[p].r <= r)        return tree[p].max;    spread(p);    int mid = tree[p].l + tree[p].r >> 1;    ll ans = 0;    if(l <= mid)        ans = max(ask_max(p<<1,l,r),ans);    if(r > mid)        ans = max(ask_max(p<<1|1,l,r),ans);    return ans;}int main(){    scanf("%d",&n);    for(int i = 1 ; i <= n ; i ++)        scanf("%d",&num[i]);    build_tree(1,1,n);    return 0;}

其它的一些奇怪的算法 | 姿势

逆序对 && 归并排序
归并排序求逆序对是唯有的几个比较高效的求逆序对算法(线段树:???),前几年noip有用到。
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define ll long longusing namespace std;const int sz = 2000100;ll ans;int n;int num[sz];int temp[sz];void merge_sort(int l,int r){    if(l == r)        return ;    int mid = l + r >> 1;    merge_sort(l,mid) , merge_sort(mid+1,r);    int p = l , pl = l , pr = mid + 1;    while(pl <= mid || pr <= r)    {        if(pr > r || (pl <= mid && num[pl] <= num[pr]))            temp[p ++] = num[pl ++];        else            temp[p ++] = num[pr ++] , ans += mid - pl + 1;    }    for(int i = l ; i <= r ; i ++)        num[i] = temp[i];    return ;}int main(){    int n;    scanf("%d",&n);    for(int i = 1 ; i <= n ; i ++)        scanf("%d",&num[i]);    merge_sort(1,n);    printf("%lld\n",ans);    return 0;}

状压搜索,noip还真没见过,模拟赛里倒是不少。
以HAOI2008移动玩具为例。
这里写图片描述

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<queue>using namespace std;bool use[2000100];struct gtnd{    int k;    int now;};queue < gtnd > q;int st,ed;void start_work(){    for(int i = 0 ; i < 16 ; i ++)    {        char ins = getchar();        while(ins != '1' && ins != '0')            ins = getchar();        ins -= '0';        st |= ( ins << i );    }    for(int i = 0 ; i < 16 ; i ++)    {        char ins = getchar();        while(ins != '1' && ins != '0')            ins = getchar();        ins -= '0';        ed |= ( ins << i );    }}int bfs(){    gtnd star;    star.k = st;    star.now = 0;    q.push(star);    use[st] = 1;    while(!q.empty())    {        gtnd f = q.front();        q.pop();        if(f.k == ed)            return f.now;        for(int i = 0 ; i < 16 ; i ++)        {            if((f.k >> i) & 1)            {                if(i > 3 && !((f.k >> (i-4)) & 1))                {                    gtnd nxt;                    nxt.now = f.now + 1;                    nxt.k = f.k;                    nxt.k ^= (1 << i-4);                    nxt.k ^= (1 << i);                    if(!use[nxt.k])                    {                        use[nxt.k] = 1;                        q.push(nxt);                    }                }                if(i < 12 && !((f.k >> (i+4)) & 1))                {                    gtnd nxt;                    nxt.now = f.now + 1;                    nxt.k = f.k;                    nxt.k ^= (1 << i+4);                    nxt.k ^= (1 << i);                    if(!use[nxt.k])                    {                        use[nxt.k] = 1;                        q.push(nxt);                    }                }                if(i % 4 != 0 && !((f.k >> (i-1)) & 1))                {                    gtnd nxt;                    nxt.now = f.now + 1;                    nxt.k = f.k;                    nxt.k ^= (1 << i-1);                    nxt.k ^= (1 << i);                    if(!use[nxt.k])                    {                        use[nxt.k] = 1;                        q.push(nxt);                    }                }                if(i % 4 != 3 && !((f.k >> (i+1)) & 1))                {                    gtnd nxt;                    nxt.now = f.now + 1;                    nxt.k = f.k;                    nxt.k ^= (1 << i+1);                    nxt.k ^= (1 << i);                    if(!use[nxt.k])                    {                        use[nxt.k] = 1;                        q.push(nxt);                    }                }            }        }    }    return -1;}int main(){    start_work();    printf("%d\n",bfs());    return 0;}

论如何正确地打开脑洞
这里写图片描述
自己的码力实现不了的做法还是少想;
看数据范围估计复杂度,看有没有套路,有没有可以套的模型,看特殊的条件等等。

9 0
原创粉丝点击