• SPOJ 3267: DQUERY 树状数组,离线算法


    给出q个询问,询问一段区间里面的不同元素的个数有多少个。

    离线做,用树状数组。

    设树状数组的意义是:1--pos这个段区间的不用元素的种类数。怎么做?就是add(pos,1);在这个位置中+1,就是说这个位置上元素种类+1。

    然后先把询问按R递增的顺序排序。因为这里是最优的,我每次尽量往R靠,使得查询不重不漏。

    什么意思呢?

    就是假如有:2、1、3、5、1、7的话。

    一开始的[1,4]这段数字全部压进树状数组,用个数组book[val],表示val这个元素出现的最右的位置,因为我们需要删除重复的,也是要尽量往右靠。到达pos=5这个位置的时候,注意了,因为1是出现过的book[1] = 2,所以我们要做的是把2这个位置出现元素的种类数-1,就是add(book[1],-1)。然后把第五个位置出现的元素种类数+1,就是add(5,1)。为什么呢?因为你尽量把种类往右靠,因为我们的R是递增的,这样,你使得查询[4,6]成为可能,因为我那个1加入来了,而不是一直用pos=2那个位置的1,再者,查询[4,7]的话,一样的意思,因为中间的1进来了。所以我们因为尽量往右靠,毕竟我们都把query按R排序了。

    还有这个只能离线,一直预处理ans[i]表示第i个询问的ans。更新到[4,7]后,查询[1,2]已经不可能了,因为很明显,pos=2这个位置已经被删除了。

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    #define inf (0x3f3f3f3f)
    typedef long long int LL;
    
    #include <iostream>
    #include <sstream>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <string>
    const int maxn = 1e6+20;
    int book[maxn];
    int c[maxn];//树状数组,多case的记得要清空
    int n; //我只需记录这个位置是不是新元素,不用离散
    int lowbit (int x)//得到x二进制末尾0的个数的2次方 2^num
    {
        return x&(-x);
    }
    void add (int pos,int val)//在第pos位加上val这个值
    {
        while (pos<=n) //n是元素的个数
        {
            c[pos] += val;        pos += lowbit(pos);
        }
        return ;
    }
    int get_sum (int pos) //求解:1--pos的总和
    {
        int ans = 0;
        while (pos)
        {
            ans += c[pos];        pos -= lowbit(pos);
        }
        return ans;
    }
    int a[maxn];
    struct node
    {
        int L,R,id;
        bool operator < (const struct node &rhs) const
        {
            if (R != rhs.R) return R < rhs.R;
            else return L < rhs.L;
        }
    }query[maxn];
    int ans[maxn];
    void work ()
    {
        scanf("%d",&n);
        for (int i=1;i<=n;++i) scanf("%d",&a[i]);
        //树状数组含义:1--pos间的不同元素种类数
        int q; scanf("%d",&q);
        for (int i=1;i<=q;++i)
        {
            scanf("%d%d",&query[i].L,&query[i].R);
            query[i].id = i; //记录ans
        }
        sort(query+1,query+1+q);
        int cur = 1;
        //book[val]的含义,val这个元素出现的最右边的位置
        for (int i=1;i<=q;++i)
        {
            for (int j=cur;j<=query[i].R;++j)
            {
                if (book[a[j]]) //这个元素在之前位置出现过,我们尽量往右,所以先删除那个位置的种类数,更新现在这个位置的种类数。因为这样的话,使得查询[4,5]是可能的
                    add(book[a[j]],-1); //del 这个位置
                book[a[j]]=j; //更新这个位置的最右值
                add(j,1); //这个位置出现了新元素
            }
            cur = query[i].R+1;
            ans[query[i].id] = get_sum(query[i].R) - get_sum(query[i].L-1);
        }
        for (int i=1;i<=q;++i)
            printf ("%d
    ",ans[i]);
    }
    
    int main()
    {
    #ifdef local
        freopen("data.txt","r",stdin);
    #endif
        work();
        return 0;
    }
    View Code

    HDU 3333 Turing Tree

    一样的,只不过是求元素的和。。把add那里的val改成a[i]即可,幻想它出现了val次,一统计,就是了。

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    #define inf (0x3f3f3f3f)
    typedef long long int LL;
    
    #include <iostream>
    #include <sstream>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <string>
    const int maxn = 30000 + 20;
    LL c[maxn];//树状数组,多case的记得要清空
    int n;
    LL a[maxn];
    struct node
    {
        int L,R,id;
        bool operator < (const struct node &rhs) const
        {
            return R < rhs.R;
        }
    }query[2*100000];
    LL ans[2*100000];
    map<LL,int>book;
    int lowbit (int x)//得到x二进制末尾0的个数的2次方 2^num
    {
        return x&(-x);
    }
    void add (int pos,LL val)//在第pos位加上val这个值
    {
        while (pos<=n) //n是元素的个数
        {
            c[pos] += val;        pos += lowbit(pos);
        }
        return ;
    }
    LL get_sum (int pos) //求解:1--pos的总和
    {
        LL ans = 0;
        while (pos)
        {
            ans += c[pos];        pos -= lowbit(pos);
        }
        return ans;
    }
    void init ()
    {
        memset(c,0,sizeof c);
        book.clear();
        return ;
    }
    void work ()
    {
        init();
        scanf("%d",&n);
        for (int i=1;i<=n;++i) scanf("%I64d",&a[i]);
        int q;
        scanf ("%d",&q);
        for (int i=1;i<=q;++i)
        {
            scanf("%d%d",&query[i].L,&query[i].R);
            query[i].id = i;
        }
        sort(query+1,query+1+q);
        int cur=1;
        for (int i=1;i<=q;++i)
        {
            for (int j=cur;j<=query[i].R;++j)
            {
                int t = book[a[j]];
                if (t)
                {
                    add(t,-a[j]);
                }
                book[a[j]]=j;
                add(j,a[j]);
            }
            cur = query[i].R+1;
            ans[query[i].id] = get_sum(query[i].R) - get_sum(query[i].L-1);
        }
        for (int i=1;i<=q;++i)
        {
            printf ("%I64d
    ",ans[i]);
        }
    }
    
    int main()
    {
    #ifdef local
        freopen("data.txt","r",stdin);
    #endif
        int t;
        scanf ("%d",&t);
        while (t--) work();
        return 0;
    }
    View Code
  • 相关阅读:
    mvc razor中renderPartial,RenderAction,Partial,Action的使用选择
    jquery最常用的几个方法。——可删除
    配置文件参数引用
    tinkphp5.0目录结构说明
    ionic ios上状态栏和app重叠解决方案
    cordova-plugin-alipay-v2使用篇(更新至20170725)(亲测可用)
    Ionic2 App Import BrowserAnimationsModule or NoopAnimationsModule问题
    ionic3.0 中带顶部导航的下拉刷新列表的实现
    npm install 时 提示err code EINTEGRITY报错
    ionic3.0 alipay-base插件移除后会添加多余的链接文件在nodes-modules中,导致再安装其他插件或移除插件时报错问题
  • 原文地址:https://www.cnblogs.com/liuweimingcprogram/p/5805076.html
Copyright © 2020-2023  润新知