几道线段树题目

总结一下这段时间所写的线段树题目,觉得好久没写博客了 =、=

 

UVA  12299 RMQ with Shifts

题目链接:http://vjudge.net/contest/view.action?cid=51165#problem/H

 

思路:直接上的挑战程序上的模板,需要注意输入格式及这种开区间写法带来的细节问题

 

 

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<sstream>
#include<cmath>
#include<climits>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<ctime>

using namespace std;

int n;
int dat[305000];

void init(int n_)
{
    n = 1;
    while(n < n_)
        n *= 2;
    for(int i = 0; i < 2 * n - 1; i++)
        dat[i] = INT_MAX;
}

void update(int k, int a)
{
    k += n - 1;
    dat[k] = a;
    while(k > 0)
    {
        k = (k - 1) / 2;
        dat[k] = min(dat[2 * k + 1], dat[2 * k + 2]);
    }
}

int query(int a, int b, int k, int l, int r)
{
    if(r <= a || b <= l)
        return INT_MAX;
    if(a <= l && r <= b)
        return dat[k];
    else
    {
        int vl = query(a, b, 2 * k + 1, l, (l + r) / 2);
        int vr = query(a, b, 2 * k + 2, (l + r) / 2, r);
        return min(vl, vr);
    }
}

int b[200000];

int main()
{
    int q, n_;
    while (~scanf("%d%d", &n_, &q))
    {
        init(n_);
        char s[20];
        for(int i = 0; i < n_; i++)
        {
            int a;
            scanf("%d", &a);
            update(i, a);
        }
        while(q--)
        {
            scanf(" %c%*c%*c%*c%*c", s);
            if(s[0] == 'q')
            {
                int x, y;
                scanf("(%d,%d)", &x, &y);
                printf("%d\n", query(x - 1, y, 0, 0, n));
            }
            else
            {
                char c;
                int i = 0;
                scanf("(");
                while(scanf("%d%c", &b[i], &c))
                {
                    b[i]--;
                    i++;
                    if(c == ')')
                        break;
                }
                i--;
                int t = dat[b[0] + n - 1];
                for(int j = 0 ; j < i; j++)
                {
                    update(b[j], dat[b[j + 1] + n - 1]);
                }
                update(b[i], t);
            }
        }
    }
    return 0;
}

 

 

 

 

 

 

 

 

HDU 3974  Assign the task

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3974

 

思路:C x表示查询x节点的值,T x y表示更新x节点及其子节点的值为y,使用先序遍历和懒惰标记(懒惰标记指在进行区间更新时,只更新父节点的值,这样在之后查询时找到维护区间的父节点即可,而在以后需要更新区间或是查询时,如果找到被标记的点,而该点并非要找的那个,我们就对其儿子节点进行更新,并将儿子节点标记,父节点改为不标记)

 

 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;

int ln[50010], rn[50010], ji = 0, sd[500000], dn;
vector<int> gra[50010];

void dfs(int x)
{
    ln[x] = ji++;
    for (int i = 0; i < gra[x].size(); ++i)
        dfs(gra[x][i]);
    rn[x] = ji;
}

void update(int k, int l, int r, int a, int b, int t)
{
    if (r <= a || l >= b)
        return ;
    if (l >= a && r <= b)
    {
        sd[k] = t;
        return ;
    }
    if (sd[k] != -2)
    {
        sd[2 * k + 1] = sd[2 * k + 2] = sd[k];
        sd[k] = -2;
    }
    update(2 * k + 1, l, (l + r) / 2, a, b, t);
    update(2 * k + 2, (l + r) / 2, r, a, b, t);
}

int query(int k, int l, int r, int dd)
{
    if (sd[k] != -2)
        return sd[k];
    if (dd < (l + r) / 2)
        return query(2 * k + 1, l, (l + r) / 2, dd);
    else
        return query(2 * k + 2, (l + r) / 2, r, dd);
}

int main()
{
    int t;
    scanf("%d", &t);
    for (int cas = 1; cas <= t; ++cas)
    {
        printf("Case #%d:\n", cas);
        memset(sd, -1, sizeof(sd));
        int n, i, bos = 0, m;
        scanf("%d", &n);
        for (dn = 1; dn < n; dn *= 2);
        for (i = 0; i <= n; ++i)
        {
            bos += i;
            gra[i].clear();
        }
        ji = 0;
        for (i = 0; i < n - 1; ++i)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            bos -= x;
            gra[y].push_back(x);
        }
        dfs(bos);
        scanf("%d", &m);
        while (m--)
        {
            char ch[10];
            scanf(" %s", ch);
            if (*ch == 'T')
            {
                int a, b;
                scanf("%d%d", &a, &b);
                update(0, 0, dn, ln[a], rn[a], b);
            }
            else
            {
                int a;
                scanf("%d", &a);
                printf("%d\n", query(0, 0, dn, ln[a]));
            }
        }
    }
    return 0;
}

 

 

HDU 3333  Turing Tree

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3333

 

思路:由于数据过大,故需要离散化处理,可以使用map来映射,另外需注意处理重复数字

 

 

#include <iostream>
#include <cstdio>
#include <map>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;
const int maxn = 100010;

ll bit[maxn], ans[maxn];
int N, M, cnt, d[maxn];
map <int, int> m;

struct node
{
    int l;
    int r;
    int id;
} a[maxn];

int cmp(node a, node b)
{
    return a.r < b.r;
}

void add(int i, int d)
{
    while(i <= N)
    {
        bit[i] += d;
        i += i & (-i);
    }
}

ll sum(int i)
{
    ll s = 0;
    while(i)
    {
        s += bit[i];
        i -= i & (-i);
    }
    return s;
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int k = 1;
        memset(bit, 0, sizeof(bit));
        m.clear();
        scanf("%d", &N);
        for(int i = 1; i <= N; i++)
            scanf("%d", &d[i]);
        scanf("%d", &M);
        for(int i = 0; i < M; i++)
        {
            scanf("%d%d", &a[i].l, &a[i].r);
            a[i].id = i;
        }
        sort(a, a + M, cmp);
        for(int i = 0; i < M; i++)
        {
            for(; k <= a[i].r; k++)
            {
                if(m[d[k]])
                    add(m[d[k]], -d[k]);
                m[d[k]] = k;
                add(k, d[k]);
            }
            ans[a[i].id] = sum(a[i].r) - sum(a[i].l - 1);
        }
        for(int i = 0; i < M; i++)
            printf("%I64d\n", ans[i]);
    }
    return 0;
}

 

 

 

 

 

POJ 3468  A Simple Problem with Integers

题目链接:http://poj.org/problem?id=3468

 

思路:用两个树状数组分别维护一个区间内所有元素共同加上的值,两一个维护除去所加值之外其他元素的和

 

 

#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#include <math.h>
#include <algorithm>

const int maxn = 200100;

using namespace std;
typedef long long ll;

ll bit1[maxn], bit2[maxn];
int n,q;

ll sum(ll * b, int i)
{
    ll s = 0;
    while(i)
    {
        s += b[i];
        i -= i & (-i);
    }
    return s;
}

void add(ll * b, int i, int v)
{
    while(i <= n)
    {
        b[i] += v;
        i += i & (-i);
    }
}

int main()
{
    while(~scanf("%d%d",&n,&q))
    {
        for(int i = 1; i <= n; i++)
        {
            int a;
            scanf("%d", &a);
            add(bit1, i, a);
        }
        for(int i = 0; i < q; i++)
        {
            char c;
            int l, r, x;
            scanf(" %c", &c);
            if(c == 'C')
            {
                scanf("%d%d%d", &l, &r, &x);
                add(bit1, l, (-x) * (l - 1));
                add(bit2, l, x);
                add(bit1, r + 1, x * r);
                add(bit2, r + 1, -x);
            }
            else
            {
                scanf("%d%d",&l,&r);
                ll res = 0;
                res += sum(bit1, r) + sum(bit2, r) * r;
                res -= sum(bit1, l - 1) + sum(bit2, l - 1) * (l - 1);
                printf("%I64d\n", res);
            }
        }
    }
}

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值