线段树——单点更新(二)

HDU 4217 Data Structure? 

http://acm.hdu.edu.cn/showproblem.php?pid=4217

CZ做的一道题目,我帮忙看了看。

题意:给定N个数(1---N),K个操作,然后给K数,i1,i2,i3.......ik 每次取走当前状态下等的第i个数(从小到大的),问取走的数的和。

给的那个的n为262144可以断定肯定是O(n*log(n))级的算法,所以选定线段树。

叶子节点赋值为1,这些节点存储的就是数的个数,在添加一个val数组记录是哪个数。每次更新将节点a[rt]变成0,找出对应的val[rt]即可。

这里坑爹的地方时sum求和时,要用—int64.wa了很多次才发现。

View Code
#include <iostream>

#include <cstring>

#include <cstdio>

#define maxn 262145

using namespace std;



int a[maxn*4],val[maxn*4];

int tmp;

__int64 sum;



void pushup(int rt)

{

    a[rt] = a[rt<<1] + a[rt<<1|1];

}

void build(int l,int r,int rt)

{

    if (l == r)

    {

        a[rt] = 1;

        val[rt] = ++tmp;

        return ;

    }

    int m = (l + r)>>1;

    build(l,m,rt<<1);

    build(m + 1,r,rt<<1|1);

    pushup(rt);

}

void update(int sc,int l,int r,int rt)

{

    if (l == r)

    {

        a[rt] = 0;

        sum += val[rt];

        return ;

    }

    int m = (l + r)>>1;

    if (sc <= a[rt<<1]) update(sc,l,m,rt<<1);

    else

    {

        sc -= a[rt<<1];

        update(sc,m + 1,r,rt<<1|1);

    }

    pushup(rt);

}

int main()

{

    int n,q,t,c;

    int cas = 1;

    scanf("%d",&t);

    while (t--)

    {

        for (int i = 0; i < 4*maxn; ++i) a[i] = val[i] = 0;

        tmp = 0;

        scanf("%d%d",&n,&q);

        sum = 0;

        build(1,n,1);

        while (q--)

        {

            scanf("%d",&c);

            update(c,1,n,1);

        }

        printf("Case %d: %I64d\n",cas++,sum);

    }

    return 0;

}

 

 hdu 4263 Juggler

http://acm.hdu.edu.cn/showproblem.php?pid=4262

题意:

可以把本题理解成一个佛珠在手里转,可以顺时针也可以逆时针,每次将手中的佛珠扣下来扔掉,给出每个佛珠扔出去的顺序,每移动一个佛珠或者扔掉一个佛珠就要耗时1s最小耗时。

思路:

单纯的模拟是O(n^2)的复杂度,肯定会超时,这里我们用线段树来维护,叶子节点0/1表示该点是否被移除,当访问当前点与要被删除的点的相对位置时,我们用线段树区间和来求,扔出后单点更新,访问下一个到达手中的点时,getpos来求。这里主要是循环圈不好想。。伤脑筋。。

View Code
#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <stack>

#include <set>

#include <map>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))

#define Min(a,b) (a) > (b)? (b):(a)

#define Max(a,b) (a) > (b)? (a):(b)



#define ll __int64

#define inf 0x7f7f7f7f

#define MOD 1000000007ll

#define lc l,m,rt<<1

#define rc m + 1,r,rt<<1|1

#define pi acos(-1.0)

#define test puts("<------------------->")

#define maxn 100010

#define N 1000005

#define M 200007

using namespace std;



int val[maxn<<2];

int pos[maxn];



void pushup(int rt)

{

    val[rt] = val[rt<<1] + val[rt<<1|1];

}

void build(int l,int r,int rt)

{

    if (l == r)

    {

        val[rt] = 1;

        return ;

    }

    int m = (l + r)>>1;

    build(lc);

    build(rc);

    pushup(rt);

}

void update(int pos,int l,int r,int rt)

{

    if (l == r)

    {

        val[rt] = 0;

        return ;

    }

    int m = (l + r)>>1;

    if (pos <= m) update(pos,lc);

    else update(pos,rc);

    pushup(rt);

}

int query(int L,int R,int l,int r,int rt)

{

    if (l >= L && r <= R) return val[rt];

    int res = 0;

    int m = (l + r)>>1;

    if (L <= m) res += query(L,R,lc);

    if (R > m) res += query(L,R,rc);

    return res;

}

int getpos(int s,int l,int r,int rt)

{

    if (l == r) return l;



    int m = (l + r)>>1;

    if (s <= val[rt<<1]) return  getpos(s,lc);

    else

    {

        s -= val[rt<<1];

        return  getpos(s,rc);

    }

}

int main()

{

    //freopen("din.txt","r",stdin);

    int n,i,a;

    while (~scanf("%d",&n))

    {

        if (!n) break;

        for (i = 1; i <= n; ++i)

        {

            scanf("%d",&a);

            pos[a] = i;//标记第a个被扔出去的编号

        }

        build(1,n,1);

        int curpos = 1;//当前在手中的点的标号

        ll ans = 0;

        for (i = 1; i <= n; ++i)

        {

            int tmp = 0;

            if (curpos == pos[i]) ans += 1;//只剩一个点了

            else if (curpos < pos[i])//1 2 3 4 5; 3要被删除

            {

                tmp = query(curpos + 1,pos[i],1,n,1);

                ans += (min(tmp,n - i + 1 - tmp) + 1);//n - i + 1 - tmp就是顺时针的个数了,这里比赛时写麻烦了

            }

            else//同理

            {

                tmp = query(pos[i],curpos - 1,1,n,1);

                ans += (min(tmp,n - i + 1 - tmp) + 1);

            }

            //printf(">>%d %d %d %d\n",tmp,n - i + 1 - tmp,Min(tmp,n - i + 1 - tmp),(Min(tmp,n - i + 1 - tmp) + 1));//这里发现了用宏定义的Min不行出错

            update(pos[i],1,n,1);

            int s = query(1,pos[i],1,n,1);

            if (s < n - i)//如果pos[i]后面还有未被删除的点

            curpos = getpos(s + 1,1,n,1);

            else//如果没有就找第一个未被删除的点

            curpos = getpos(1,1,n,1);

        }

        printf("%I64d\n",ans);

    }



    return 0;

}

 1006 hdu 4325 http://acm.hdu.edu.cn/showproblem.php?pid=4325

http://www.cnblogs.com/E-star/archive/2012/08/01/2619066.html

pku Sliding Window http://poj.org/problem?id=2823

题解:http://www.cnblogs.com/E-star/archive/2012/08/26/2657596.html

 

 

你可能感兴趣的:(线段树)