• hdu 5919 Sequence II (可持久化线段树)


    链接:http://acm.hdu.edu.cn/showproblem.php?pid=5919

    大致题意:

    给你一个长度为n的序列,q个询问,每次询问是给你两个数x,y,经过与上一次的答案进行运算会得到一个区间[x,y],假设这个区间内有k个数,对k个数第一次出现的位置进行排序取第(k+1)/2个数。

    思路:

    看题意可知要求的是区间不同数的个数和区间第k小,强制在线,

    之前正好写过求区间不同数的个数的三种解法:离线树状数组,主席树,莫队,因为这道题是强制在线,莫队和离线树状数组都不能用,这里就直接用主席树了。

    题目要求各个数第一次出现的位置,那么我们只要从后向前插入,重复出现的取消之前的标记,这样维护的标记就都是当前坐标i到n区间内第一次出现的,用主席树维护下就好了。

    用主席树求出区间不同数的个数num后, 直接再求区间第(num+1)/2小就好了

    注意数组要开大点。。之前数组开小了 超时了

    实现代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int M = 2e5 + 10;
    #define mid int m = (l + r) >> 1
    int ls[M*40],rs[M*40],sum[M*40],root[M],a[M],vis[M],idx,ans[M],n;
    
    void init()
    {
        idx = 0;root[n+1] = 0; ans[0] = 0;
        memset(ls,0,sizeof(ls));
        memset(vis,0,sizeof(vis));
        memset(rs,0,sizeof(rs));
        memset(sum,0,sizeof(sum));
    }
    
    void update(int old,int &k,int l,int r,int p,int c){
        k = ++idx;
        ls[k] = ls[old]; rs[k] = rs[old];
        sum[k] = sum[old] + c;
        if(l == r) return ;
        mid;
        if(p <= m) update(ls[old],ls[k],l,m,p,c);
        else update(rs[old],rs[k],m+1,r,p,c);
    }
    
    int query(int x,int L,int R,int l,int r){  //求区间不同数的个数
        if(L <= l&&R >= r) return sum[x];
        mid,ret = 0;
        if(L <= m) ret += query(ls[x],L,R,l,m);
        if(R > m) ret += query(rs[x],L,R,m+1,r);
        return ret;
    }
    
    int query1(int x,int l,int r,int k){   //求区间第k小
        if(l == r) return l;
        mid,ret = sum[ls[x]];
        if(ret >= k) return query1(ls[x],l,m,k);
        else return query1(rs[x],m+1,r,k - ret);
    }
    
    int main()
    {
        int t,cas = 1,q,x,y;
        scanf("%d",&t);
        while(t--){
            init();
            scanf("%d%d",&n,&q);
            for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
            for(int i = n;i >= 1;i --){
                int tmp  = 0;
                if(vis[a[i]] == 0) update(root[i+1],root[i],1,n,i,1); //添加新标记
                else{
                    update(root[i+1],tmp,1,n,vis[a[i]],-1);  //把之前的标记清掉
                    update(tmp,root[i],1,n,i,1);  //添加新标记
                }
                vis[a[i]] = i;
            }
            for(int i = 1;i <= q;i ++){
               scanf("%d%d",&x,&y);
               x = ((x + ans[i-1])%n) + 1;
               y = ((y + ans[i-1])%n) + 1;
               if(x > y) swap(x,y);  //倒着输入的,所以取较小的
               int num = (query(root[x],x,y,1,n) + 1) >> 1;  //得到中位数是区间内第num/2小的数
               ans[i] = query1(root[x],1,n,num);  //求区间内第(num+1)/2小的数
            }
            printf("Case #%d: ",cas++);
            for(int i = 1;i < q;i ++)  printf("%d ",ans[i]);
            printf("%d
    ",ans[q]);
        }
        return 0;
    }
  • 相关阅读:
    创建pdf
    IOS绘图
    IOS断点续传
    IOS程序之间的跳转
    MBProgressHUD的使用
    清除缓存的方法(计算)
    使用post请求下载数据
    NSTimer的使用
    定位功能(使用系统地图)
    fork仓库保持同步更新
  • 原文地址:https://www.cnblogs.com/kls123/p/9562875.html
Copyright © 2020-2023  润新知