可持久化线段树 / 主席树

参考博客
图解

模板
网上很多博客都有一开始建空树的操作,实际上这一步是可以省去的。省去后功能不仅不会受到影响,同时在处理多组数据的时候还能省下不少时间(见下文HDU 6621)。

#include <cstdio>
#include <algorithm>

using namespace std;
const int MAXN = 200010;
int node_cnt, n, m;
int sum[MAXN*40], rt[MAXN], lc[MAXN*40], rc[MAXN*40];//线段树相关,乘数 > log(n) 
int a[MAXN], b[MAXN];//原序列和离散序列
int p;//修改点

int modify(int pre, int l, int r)
{
    int now = ++node_cnt;
    lc[now] = lc[pre]; rc[now] = rc[pre]; sum[now] = sum[pre] + 1;
    if(l == r)
        return now;
    int mid = (l + r) >> 1;
    if(p <= mid) lc[now] = modify(lc[now], l, mid);
    else rc[now] = modify(rc[now], mid+1, r);
    return now;
}

int query(int u, int v, int l, int r, int k)
{
    int ans, mid = ((l + r) >> 1), x = sum[lc[v]] - sum[lc[u]];
    if(l == r)
        return l;
    if(x >= k) ans = query(lc[u], lc[v], l, mid, k);
    else ans = query(rc[u], rc[v], mid+1, r, k-x);
    return ans;
}

int main()
{
    int l, r, k, q, ans;
    while(~scanf("%d%d", &n, &m))
    {
    	node_cnt=0;
	    for(int i = 1; i <= n; i++)
	    {
	    	scanf("%d", &a[i]);
	    	b[i] = a[i];
	    }
	    sort(b+1, b+n+1);
	    q = unique(b+1, b+n+1) - b - 1; //离散化 
	    //build(rt[0], 1, q);
	    for(int i = 1; i <= n; i++)
	    {
	        p = lower_bound(b+1, b+q+1, a[i])-b; //p为a[i]的排名 
	        rt[i] = modify(rt[i-1], 1, q);
	    }
	    while(m--)
	    {
	        scanf("%d%d%d", &l, &r, &k);
	        ans = query(rt[l-1], rt[r], 1, q, k);
	        printf("%d\n", b[ans]);
	    }
    }
    
    return 0;
}

HDU 6601

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;
const int MAXN = 100010;
int node_cnt, n, m;
int sum[MAXN*40], rt[MAXN], lc[MAXN*40], rc[MAXN*40];//线段树相关,乘数 > log(n) 
long long a[MAXN], b[MAXN];//原序列和离散序列
int p;//修改点

/*void build(int &t, int l, int r)
{
    t = ++node_cnt;
    if(l == r)
        return;
    int mid = (l + r) >> 1;
    build(lc[t], l, mid);
    build(rc[t], mid+1, r);
}*/

int modify(int pre, int l, int r)
{
    int now = ++node_cnt;
    lc[now] = lc[pre]; rc[now] = rc[pre]; sum[now] = sum[pre] + 1;
    if(l == r)
        return now;
    int mid = (l + r) >> 1;
    if(p <= mid) lc[now] = modify(lc[now], l, mid);
    else rc[now] = modify(rc[now], mid+1, r);
    return now;
}

int query(int u, int v, int l, int r, int k)
{
    int ans, mid = ((l + r) >> 1), x = sum[rc[v]] - sum[rc[u]];
    if(l == r)
        return l;
    if(x >= k) ans = query(rc[u], rc[v], mid+1, r, k);
    else ans = query(lc[u], lc[v], l, mid, k-x);
    return ans;
}

int main()
{
    int l, r, q;
    while(~scanf("%d%d", &n, &m))
    {
    	node_cnt=0;
        for(int i = 1; i <= n; i++)
        {
            scanf("%lld", &a[i]);
            b[i] = a[i];
        }
        sort(b+1, b+n+1);
        q = unique(b+1, b+n+1) - b - 1; //离散化 
        for(int i = 1; i <= n; i++)
        {
            p = lower_bound(b+1, b+q+1, a[i])-b; //p为a[i]的排名 
            rt[i] = modify(rt[i-1], 1, q);//建树 
        }
        while(m--)
        {
            scanf("%d%d", &l, &r);
            if(r-l+1<3){
                printf("-1\n");
                continue;
            }
            int tmp1 = query(rt[l-1], rt[r], 1, q, 1);
            int tmp2 = query(rt[l-1], rt[r], 1, q, 2);
            int tmp3 = query(rt[l-1], rt[r], 1, q, 3);
            int rk = 3,flg = 1;
            while(b[tmp2] + b[tmp3] <= b[tmp1])
            {
                if(rk >= r - l + 1){
                    flg = 0;
                    break;
                }
                tmp1 = tmp2; tmp2 = tmp3;
                tmp3 = query(rt[l-1], rt[r], 1, q, ++rk);
            }
            if(flg)printf("%lld\n",b[tmp1]+b[tmp2]+b[tmp3]);
            else printf("-1\n");
            //printf("%d\n", b[ans]);
        }
    }
    
    
    return 0;
}

HDU 6621

#include <cstdio>
#include <algorithm>

using namespace std;
const int MAXN = 100010;
int node_cnt, n, m;
int sum[MAXN*40], rt[MAXN], lc[MAXN*40], rc[MAXN*40];//线段树相关,乘数 > log(n) 
int a[MAXN], b[MAXN];//原序列和离散序列
int p;//修改点

int ql,qr;
void build(int &t, int l, int r)
{
    t = ++node_cnt;
    if(l == r)
        return;
    int mid = (l + r) >> 1;
    build(lc[t], l, mid);
    build(rc[t], mid+1, r);
}

int modify(int pre, int l, int r)
{
    int now = ++node_cnt;
    lc[now] = lc[pre]; rc[now] = rc[pre]; sum[now] = sum[pre] + 1;
    if(l == r)
        return now;
    int mid = (l + r) >> 1;
    if(p <= mid) lc[now] = modify(lc[now], l, mid);
    else rc[now] = modify(rc[now], mid+1, r);
    return now;
}

int query(int u, int v, int l, int r)
{
    //int ans, mid = ((l + r) >> 1), x = sum[lc[v]] - sum[lc[u]];
    if(l >= ql && r <= qr) return sum[v] - sum[u];
    int ret = 0, mid = ((l + r) >> 1);
    if(ql <= mid) ret += query(lc[u],lc[v],l,mid);
    if(qr >= mid+1) ret += query(rc[u],rc[v],mid+1,r); 
    return ret;
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int l, r, p, k, q=1000000;
	    scanf("%d%d", &n, &m);
	    build(rt[0], 1, q);
	    for(int i = 1; i <= n; i++)
	    {
	    	scanf("%d", &a[i]);
	    	p = a[i];
	    	modify(rt[i-1],1,q);
	    }
	    int ans=0;
	    while(m--)
	    {
	        scanf("%d%d%d%d", &l, &r, &p, &k);
	        l^=ans;r^=ans;p^=ans;k^=ans;
	        int L=0,R=q,mid;
	        while(L<R)
	        {
	        	mid = (L + R) / 2;
	        	ql = max(1, p - mid);
	        	qr = min(q, p + mid);
	        	int num = query(rt[l-1], rt[r], 1, q);
	        	if(num >= k){
	        		R = mid;
	        	}else{
	        		L = mid + 1;
	        	}
	        }
	        printf("%d\n",L);
	        ans = L;
	    }
	}

    return 0;
}

在这里插入图片描述
上面的提交是省去建空树操作的,对比建空树节省将近一半的时间。
此题不建空树节省时间原因:建空树复杂度为O( 数据范围*log(数据范围)),此题由于不进行离散化操作,因此数据范围为1e6,多次建空树会消耗不少时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值