NOIP前夕模板整理第二弹:数据结构

来源:互联网 发布:淘宝网电动车阳伞 编辑:程序博客网 时间:2024/05/23 01:15

1、并查集
例题:家族
然而其实并查集的用处还有很多

#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>using namespace std;int fa[100000];int find(int x){    return fa[x] == x?x:fa[x] = find(fa[x]);}int main(){    int n,m,p;    scanf("%d%d%d",&n,&m,&p);    for(int i =1; i <= n; i ++)    fa[i] = i;    for(int i = 1; i <= m; i ++)    {        int a,b;        scanf("%d%d",&a,&b);        if(find(a) != find(b))        {            fa[find(b)] = find(a);        }    }    for(int i = 1; i <= p; i ++)    {        int a,b;        scanf("%d%d",&a,&b);        if(find(a) != find(b))        puts("No");        else        puts("Yes");    }}

2、线段树
例题:线段树练习3
支持区间修改,区间求和
(线段树练习1,2比较简单就不放了)

#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>#define MAXN 200000+5using namespace std;typedef long long ll;ll num[MAXN];struct tree{    ll sum,add,l,r;}tree[MAXN<<2];void build(ll l, ll r,ll p){    tree[p].l = l;    tree[p].r = r;    if(l == r)    {        tree[p].sum = num[l];        return ;    }    ll mid = (l+r)>>1;    build(l,mid,p*2);    build(mid+1,r,p*2+1);    tree[p].sum = tree[p*2].sum + tree[p*2+1].sum;    return ;}void spread(ll p){    if(tree[p].add)    {        tree[p*2].sum += (tree[p*2].r - tree[p*2].l + 1)*tree[p].add;        tree[p*2+1].sum += (tree[p*2+1].r - tree[p*2+1].l + 1)*tree[p].add;        tree[p*2].add += tree[p].add;        tree[p*2+1].add += tree[p].add;        tree[p].add = 0;        return ;     }}void change(ll l,ll r,ll p, ll x){    if(l <= tree[p].l && tree[p].r <= r)    {        tree[p].sum += (tree[p].r - tree[p].l + 1)*x;        tree[p].add += x;        return ;    }    spread(p);    ll mid = (tree[p].l+tree[p].r)/2;    if(l <= mid)    change(l,r,p*2,x);    if(r > mid)    change(l,r,p*2+1,x);    tree[p].sum = tree[p*2].sum + tree[p*2+1].sum;    return ;}ll ask(ll l,ll r,ll p){    if(l <= tree[p].l&& tree[p].r <= r)    {        return tree[p].sum;    }    spread(p);    ll mid = (tree[p].l + tree[p].r )/2;    ll ans = 0;    if(l <= mid)    ans += ask(l,r,p*2);    if(r > mid)    ans += ask(l,r,p*2+1);    return ans;}int main(){    ll n,q;    ll a,b,c,x,y,z;    scanf("%lld",&n);    for(int i = 1; i <= n; i ++)    {        scanf("%lld",&num[i]);    }    build(1,n,1);    scanf("%lld",&q);    int t;    for(int i = 1; i <= q; i ++)    {        scanf("%d",&t);        if(t == 1)        {            scanf("%lld%lld%lld",&a,&b,&x);            change(a,b,1,x);        }        if(t == 2)        {            scanf("%lld%lld",&y,&z);            printf("%lld\n",ask(y,z,1));        }    }    return 0;}

(线段树的代码真的很长,所以来看树状数组)
3、树状数组
关于树状数组的思想,推荐一篇比较好的博客,其实我已经转载过来了,直接看我博客里的就行,233,链接:http://blog.csdn.net/cherish_k/article/details/53066617
(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;}

最后放一个手写堆排吧,虽然还是sort快

#include <cstdio>#include <algorithm>#include <cstring>#include <iostream>using namespace std;int heap[200000],cnt;void push(int x){    cnt ++;    int now = cnt;    heap[now] = x;    while(now != 1)    {        int fa = now /2;        if(heap[now] < heap[fa])        {            swap(heap[now],heap[fa]);            now = now/2;        }        else break;    }    return ;}void pop(){    heap[1] = heap[cnt];    int now = 1;    while(now*2+1 <= cnt)    {        int l = now*2,r = now*2+1;        if(heap[l] < heap[now])        {            if(heap[r] < heap[now] && heap[r] < heap[l])            swap(l,r);            swap(heap[l],heap[now]);            now = l;        }        else if(heap[r] < heap[now])        {            swap(heap[r],heap[now]);            now = r;        }        else break;    }    cnt --;}int main(){    int n,x;    scanf("%d",&n);    for(int i = 1; i <= n; i ++)    {        scanf("%d",&x);        push(x);    }    for(int i = 1; i <= n; i ++)    {        printf("%d ",heap[1]);        pop();    }    return 0;}
1 0
原创粉丝点击