• 2021ICPC昆明M题 非主席树做法


    比赛时想出来的做法,我并不会主席树,比赛时想到这个做法时非常激动,可惜没有A出来

    题意:给一个长度为n数列a[n],m次查询,每次查询给一个区间[Li ,Ri],定义一个集合Si,Si为这个区间里选任意一些数相加得到的数,定义f(S)为集合S里没出现的最小的正数,对于每次查询,输出f(Si)

    示例:

    1 4 1 2 6

    区间为[1,3]时,构成的集合S为{1,2,4,5,6},那么f(S)为3

    数据范围:1<= n <=1e6 ; 1<=m <= 1e5 ; 1<=a[i]<=1e9;

    给定一个区间后,怎么求f(S)?

    研究了一段时间后,可以发现f(S)能这样求,首先把区间里的数排序,f(S)先初始化为1,看最小的数是否>1,如果>1,那么f(S)就为1,如果==1,那么f(S)就加上1,然后再看第二小的数是否>f(S),如果<=f(S),f(S)就加上这个数,如果>f(S),那么f(S)就确定了

    也就是f(S)可以被所有小于等于f(S)的数之和+1更新

    写成代码的话就是这样:(mex为f(S))

    int mex = 1;
    while (1) {
    	int res = 区间里所有小于等于mex的数之和+1
    	if (res <= mex) break;
    	mex = res;
    }
    

      

    但是查询有多次,我们不能用排序,否则会改变数组,所以要想办法快速求出一段区间里所有小于等于x的数之和

    这个可以用主席树来求

    但是比赛时我不会主席树,就一直在想怎么求这个

    我的做法用线段树来求一段区间里所有小于等于1,小于等于2,小于等于4,小于等于8.....小于等于2^30的数之和,一个NODE里的sum[i]表示区间里所有小于等于(1<<i)的数之和

    假设我现在mex通过所有小于等于512的数之和(记为low(512))更新到1400,这样当我要求low(1400)时,我可以先用low(1024)来近似,如果low(1024)>=2048,那么我就可以用low(2048)来更新mex了,

    那么如果low(1024)<2048呢,我怎么求low(1024)+1024到1400的数之和呢 

    我们还可以求出一段区间里所有大于1024的最小的数,记为min(1024),如果min(1024) > 1400,那么说明1024到1400之间不存在任何数,如果min(1024)<1400,那么1024到1400之间至少存在一个数,而low(1024) = 1400只要加上这个数,就一定会大于2048,所以就可以用low(2048)来更新mex了!!

    NODE结构体是这样的

    struct NODE {
    	int l, r;
    	long long su[31], mi[31];//su[i]表示所有小于等于(1<<i)的数之和, mi[i]表示所有大于(1<<i)的数之和
    }tree[MAXN*4];
    

      

    赛后AC代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    const int MAXN = 1e6 + 7;
    const long long INF = 1e9 + 7;
    struct NODE {
    	int l, r;
    	long long su[31], mi[31];
    }tree[MAXN*4];
    void upd(int pos) {
    	for (int i = 0; i < 31; i++) {
    		tree[pos].su[i] = tree[pos << 1].su[i] + tree[pos << 1 | 1].su[i];
    		tree[pos].mi[i] = min(tree[pos << 1].mi[i], tree[pos << 1 | 1].mi[i]);
    	}
    }
    void build(int pos, int l, int r) {
    	tree[pos].l = l; tree[pos].r = r;
    	if (l == r) {
    		long long v;
    		scanf("%lld",&v);
    		for (int i = 0; i < 31; i++) {
    			if (v > ((long long)1 << i)) {
    				tree[pos].su[i] = 0;
    			}
    			else tree[pos].su[i] = v;
    		}
    		for (int i = 0; i < 31; i++) {
    			if (v <= ((long long)1 << i)) tree[pos].mi[i] = INF;
    			else tree[pos].mi[i] = v;
    		}
    		return;
    	}
    	int mid = l + r >> 1;
    	build(pos<<1, l, mid);
    	build(pos<<1|1, mid + 1, r);
    	upd(pos);
    }
    long long Q(int pos, int l, int r, int i) {
    	if (tree[pos].l == l && tree[pos].r == r) return tree[pos].su[i];
    	int mid = tree[pos].l + tree[pos].r >> 1;
    	if (r <= mid) return Q(pos<<1, l, r, i);
    	else if (l > mid) return Q(pos<<1|1, l, r, i);
    	else return Q(pos<<1, l, mid, i) + Q(pos<<1|1, mid + 1, r, i);
    }
    long long Q_mi(int pos, int l, int r, int i) {
    	if (tree[pos].l == l && tree[pos].r == r) return tree[pos].mi[i];
    	int mid = tree[pos].l + tree[pos].r >> 1;
    	if (r <= mid) return Q_mi(pos << 1, l, r, i);
    	else if (l > mid) return Q_mi(pos << 1 | 1, l, r, i);
    	else return min(Q_mi(pos << 1, l, mid, i), Q_mi(pos << 1|1, mid + 1, r, i));
    }
    int main()
    {
    	int n, m;
    	cin >> n >> m;
    	build(1, 1, n);
    	int l, r;
    	long long ans, mex;
    	for (int i = 1; i <= m; i++) {
    		scanf("%d%d", &l, &r);
    		l = ((long long)l + ans) % n + 1;
    		r = ((long long)r + ans) % n + 1;
    		if (l > r) swap(l, r);
    		mex = 1;
    		for (int i = 0; i < 31; i++) {
    			if (mex >= ((long long)1 << i)) mex = max(mex, Q(1, l, r, i) + 1);
    			else if(i){
    				long long lim = Q_mi(1, l, r, i - 1);
    				if (lim != INF && lim > mex) break;
    				else {
    					mex = max(mex, Q(1, l, r, i) + 1);
    				}
    			}
    		}
    		ans = mex;
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    

      

    至于比赛时我为什么没能过这题,原因是爆空间了QAQ,1个G的内存给我MLE了。。。

    比赛时我用的是指针写法的线段树,NODE节点里有比较多东西阿巴阿巴。。

  • 相关阅读:
    ruby学习系列(1)
    学习调用WCF服务的各种方法
    Web Service简介
    ajax编程
    ReportView控件的使用
    .NET中26个优化性能方法
    图书管理前端页面
    图书管理后端接口
    Vue组件
    axios登录前端
  • 原文地址:https://www.cnblogs.com/ruanbaitql/p/14620031.html
Copyright © 2020-2023  润新知