• ZZUOJ 10508: 数列游戏IV


    题目链接http://acm.zzu.edu.cn:8000/problem.php?id=10508

    题目大意:给定一个序列,长度为N,每次询问为一组区间[Li,Ri],输出Li到Ri中出现恰好两次的不同数的个数. N,M<=2*10^5,序列中元素<=10^9

    解题思路:考虑用树状数组解决(大概是一种类型的题目)。树状数组一般用来快速计算更新(logN)前缀和,而对于本题来说,出现次数显然不能单纯随意相加相减,另外,对于右区间靠前的查询来说,对其查询之后后面的数据更新是不会再影响到它的,因此可以离线处理,并且需要在更新的时候针对重复元素进行一些处理。

    首先考虑一个元素在序列中不同位置重复出现的情况,如下:

    _ x _ x _ x _ x _ x _ (下划线表示出现了若干与x不相同的数字)

    从前到后给每个x编号1,2,3,4,5,下面看一下从前到后扫面到这五个位置时如何更新(其中a,b等字母表示这个位置应当具有的值):

      _ x _ x _ x _ x _ x _ 

    1  0

    2  a   b       那么应当有 b + a = 1, (b + a) - a = 0, 则 b = 0, a = 1 

    3  a   b   c       那么应当有 c + b + a = 0, (c + b + a) - (b + a) = 0, (c + b + a) - a = 1, 则 c = 0, b = 1,  a = -1.

    4  a   b   c   d    那么应当有 d + c + b + a = 0, d + c + b + a - (c + b + a) = 0, (d + c + b + a) - (b + a) = 1

                 (d + c + b + a) - a = 0, 则 d = 0, c = 1, b = -1, a = 0

    ...

    即是:

      _ x _ x _ x _ x _ x _ 

    1  0

    2  1   0         

    3   -1   1   0         

    4  0   -1   1   0     

    5  0    0   -1   1   0

    然后关系就非常明显了,我们只需要记录下每个位置的数字上次出现的位置,然后 lastpos + 1,la_lastpos - 2, la_la_lastpos + 1, 即可。那么对于任意一个区间来说,由于其中每个数字都满足互相加减的条件,因此直接树状数组相加减即可。

    大致过程:记录每个位置对应数字上次出现位置;将查询的区间按照有端点排序;从1~N枚举每个位置,按上述方法更新树状数组,然后计算以这个位置为右端点结束的区间的值。

    代码:

     1 const int maxn = 2e5 + 10;
     2 struct node{
     3     int l, r, id;
     4     bool operator < (const node& t) const{
     5         return r < t.r;
     6     }
     7 };
     8 node range[maxn];
     9 int n, m;
    10 int a[maxn], ans[maxn], bit[maxn];
    11 int last[maxn];
    12 map<int, int> mmp;
    13 
    14 int lowbit(int x){
    15     return x & (-x);
    16 }
    17 void add(int x, int v){
    18     while(x <= n){
    19         bit[x] += v;
    20         x += lowbit(x);
    21     }
    22 }
    23 int sum(int x){
    24     int ans = 0;
    25     while(x > 0){
    26         ans += bit[x];
    27         x -= lowbit(x);
    28     }
    29     return ans;
    30 }
    31 void solve(){
    32     memset(last, 0, sizeof(last));
    33     memset(bit, 0, sizeof(bit));
    34     for(int i = 1; i <= n; i++){
    35         last[i] = mmp[a[i]];
    36         mmp[a[i]] = i;
    37     }
    38     sort(range + 1, range + 1 + m);
    39     int ind = 1;
    40     for(int i = 1; i <= n; i++){
    41         if(last[i] != 0){
    42             int la = last[i];
    43             add(la, 1);
    44             if(last[la] != 0){
    45                 int lla = last[la];
    46                 add(lla, -2);
    47                 if(last[lla] != 0)
    48                     add(last[lla], 1);
    49             }
    50         }
    51         while(ind <= m && range[ind].r == i){
    52             int tml = range[ind].l, tmr = range[ind].r;
    53             ans[range[ind].id] = sum(tmr) - sum(tml - 1);
    54             ind++;
    55         }
    56     }
    57     for(int i = 1; i <= m; i++){
    58         printf("%d
    ", ans[i]);
    59     }
    60 }
    61 int main(){
    62     scanf("%d %d", &n, &m);
    63     for(int i = 1; i <= n; i++)
    64         scanf("%d", a + i);
    65     for(int i = 1; i <= m; i++){
    66         scanf("%d %d", &range[i].l, &range[i].r);
    67         range[i].id = i;
    68     }
    69     solve();
    70 }

    题目:

    10508: 数列游戏IV

    Time Limit: 1 Sec  Memory Limit: 128 MB
    Submit: 32  Solved: 6
    [Submit][Status][Web Board]

    Description

    给定一个序列,长度为N,每次询问为一组区间[Li,Ri],输出Li到Ri中出现恰好两次的不同数的个数.

    Input

    第一行两个整数N和M,N表示序列长度,M表示询问次数.(N,M<=2*10^5)
    第二行N个整数,表示序列.(序列中元素<=10^9)
    以后M行,每行为Li和Ri,表示询问区间.(1<=Li<=Ri<=N)
     

    Output

    对于每组询问,输出一行一个整数,表示不相同数的个数.

    Sample Input

    5 1
    1 2 1 1 1
    1 3

    Sample Output

    1

    HINT

     

    Source

  • 相关阅读:
    wenbao与powershell
    wenbao与windows
    wenbao与msf
    CCF201612-Python题解
    语不惊人死不休
    为人性僻耽佳句(一)
    Pytorch出现 raise NotImplementedError
    CNN卷积
    python字符串切片
    python----numpy(持续更新)
  • 原文地址:https://www.cnblogs.com/bolderic/p/7527223.html
Copyright © 2020-2023  润新知