ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2022.5.17 比赛题整理

2022-07-20 08:35:48  阅读:163  来源: 互联网

标签:比赛 17 int rep mid 中位数 num 2022.5 ux


2022.5.17 2022初一测试六

链接集合

总结

T1:二分 + 贪心。

T2:模拟(对某某数之积之和的简化与运算)。

\({\color{Red}{\text{[主席树 好题] }}}\)T3:二分求区间中位数 + 主席树维护。

T4:?(目前还未改。)

Problem A

“打破定式思维”,题面不含“最小(大)值最大(小)”时也有可能是用二分做。

二分所用秒数,然后贪心验证是否可行即可。

#include<bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for(register int i = a; i <= b; ++i)
const int maxn = 1e5 + 5;
int n;
double k, a[maxn];
double l, r, mid;

inline bool chck(double t){
	double x, s, y;
	s = a[1] + t;
	rep(i, 2, n){
		x = a[i] - t, y = a[i] + t;
		if(s + k < x) return 0;
		if(s + k > y) s = y;
		else s += k;
	}
	return 1;
}

int main(){
	scanf("%lf", &k);
	scanf("%d", &n);
	rep(i, 1, n) scanf("%lf", &a[i]);
	l = 0.0, r = 99999999.0;
	while(r - l > 0.0001){
		mid = (l + r) / 2.0;
		if(chck(mid)) r = mid;
		else l = mid;
	}
	printf("%.3f", r);
	return 0;
}

Problem B

\((x + z)* (Num_x +Num_z) = x* (Num_x + Num_z) + z* (Num_x + Num_z)\)

而对于题目中对 \(x\) 和 \(z\) 的限制我们可以简化为:下标同奇偶,所染颜色相同。(这大概是此题唯一难点。

所以我们就可以在输入的同时处理出:下标奇偶相同、颜色相同的位置所代表的数之和以及这些位置的数量分别是多少。

这样我们就可以遍历 1~n,对于第 \(i\) 个位置,计算所有 \(i* (Num_i + Num_z)\) 的总和。

注意取模,要点见注释。

#include<bits/stdc++.h>
using namespace std;
 
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
const int maxn = 1e5 + 5;
const int mod = 10007;
int n, m;
int a[maxn], c[maxn];
int num[maxn][2], tot[maxn][2];
int ans;
 
int main(){
    scanf("%d%d", &n, &m);
    rep(i, 1, n) scanf("%d", &a[i]), a[i] %= mod;
    rep(i, 1, n){
        scanf("%d", &c[i]);
        num[c[i]][i % 2] += a[i];
        tot[c[i]][i % 2] += 1;
    }
    rep(i, 1, n){
        ans += (i % mod * ((num[c[i]][i % 2] + 
        (tot[c[i]][i % 2] - 2)
        \*减去的2种:一个是前面 num 数组中统计过一次 a[i] 了,另一个是减去 i 和自己搭配的情况*\ 
        % mod * a[i] + mod) % mod)) % mod; 
    }
    printf("%d\n", ans % mod);
    return 0;
}

Problem C

非常非常好的一道主席树的题目。(写了三天才 a

1 求中位数

拿到题目首先肯定会去思考怎么求区间中位数。

按照以往求中位数的方法——对顶堆,显然不行,时间肯定会炸。

那就要引入一个新的求中位数的方法了:二分中位数大小,然后将大于等于该数的数的值设为 1,否则设为 -1,然后求区间的和,若大于等于 0,则意味着真正的中位数一定大于等于该数,否则小于该数,时间复杂度同对顶堆一样,\(O(nlogn)\)。

2 线段树维护

这时候我们考虑到题目的另一个限制:左端点在 \([a,b]\) 间,右端点在 \([c,d]\) 间。

唯一能够直接确定的是区间和一定包含 \([b+1,c-1]\) 的和。

那么为了让中位数最大,我们就要尽量让区间和最大,所以对于区间 \([a,b]\),我们取和最大的后缀,同理,区间 \([c,d]\) 取和最大的前缀。

确定了如何选取区间内的数,我们考虑如何快速求出它们。

区间求和...不难想到线段树。

再去优化一下,对初始数组进行离散化,那么二分出的中位数的范围就在 1~n 之间。

所以对于每个二分出的 \(mid\),我们构造一颗线段树,用来求上述的区间和/区间最大前后缀。

每次 \(check(mid)\) 的复杂度就变成了 \(O(logn)\)。

3 空间优化——主席树维护

这样一来,空间会炸。

那么同时在多棵线段树上进行空间优化...不难想到主席树。考虑怎样转化为主席树。

我们试着去找离散化后的数组 \(num[]\) 每个位置之间的相同与联系。

我们发现,假如我们已经对当 \(mid=num[i]\) 这种情况按照 2 中所说建立了一颗线段树(比它大(等于)的点权值设为 1,否则设为 -1),那么此时要想再建立一颗 \(mid = num[i+1]\) 这种情况的线段树(\(i\) 和 \(i+1\) 均在离散化后的数组 \(num[]\) 中,即满足单调递增),我们只需要将值为 \(num[i]\) 的点在线段树中的权值改为 -1 即可,其余的值不变。

有了上述结论,我们就可以将线段树转化为主席树去优化了。

更详细地,对于 \(rt_i\),以它为根节点的线段树内部,值小于 \(num_i\) 的点权值为 -1,否则为 1。

那么 \(rt_1\) 内部的点的点权没有值为 -1 的情况。

4 总结/思考

重新回顾这道题,发现看到题面首先容易把注意力放在“区间不定”上,但是实际上突破口在于如何求解中位数。在确定怎么求解中位数之后,将“区间不定”转化为“区间确定”也就迎刃而解了。

我们还会发现,随着思路的不断深入,我们先是优化时间复杂度,再是逐渐去优化空间复杂度,但不论优化哪个,都不会是一蹴而就的。

最后,此解法时间复杂度 \(O(nlogn^2)\),空间复杂度 \(O(nlogn)\)。

5 代码

还有一点处理主席树部分的细节需要见代码注释。

#include<bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for(register int i = a; i <= b; ++i)
#define ls t[x].l
#define rs t[x].r
#define s(x) t[x].sum
#define lx(x) t[x].lmx
#define rx(x) t[x].rmx
const int maxn = 2e4 + 5;
int n, m, lst;
int x1, x2, x3, x4;
int ux[15];
int a[maxn], num[maxn], tot;
vector <int> pos[maxn];
int rt[maxn];
struct tree{
	int l, r;
	int sum, lmx, rmx;
	bool el, er;//若左儿子指向的是以前建的节点那么 el=0,否则即新建立了一个左儿子,那么 el=1 
}t[800005];
int cnt, ans;

inline int plc(int x){
	return lower_bound(num + 1, num + tot + 1, x) - num;
}

inline void up(int x){
	s(x) = s(ls) + s(rs);
	lx(x) = max(lx(ls), s(ls) + lx(rs));
	rx(x) = max(rx(rs), s(rs) + rx(ls));
}

inline int build(int l, int r){
	int x = ++cnt;
	if(l == r){
		if(plc(a[l]) <= 1) //是否真的需要这个判断???
			s(x) = lx(x) = rx(x) = 1;
		else 
			s(x) = lx(x) = rx(x) = 1;
		return x;
	}
	int mid = l + r >> 1;
	t[x].el = t[x].er = 1;
	ls = build(l, mid), rs = build(mid + 1, r);
	up(x);
	return x;
}

inline int update(int x, int y, int l, int r, int k, int c, bool f){
	if(!f) x = ++cnt;
	if(l == r){
		s(x) = lx(x) = rx(x) = c;
		return x;
	}
	int mid = l + r >> 1;
	if(k <= mid){
		if(!t[x].er) rs = t[y].r;
		if(!t[x].el)
			t[x].el = 1,
			ls = update(x, t[y].l, l, mid, k, c, 0);
			//如果要修改的节点在左子树,我们要新建一个左子树,而不是直接修改覆盖以前的信息 
		else
			ls = update(ls, t[y].l, l, mid, k, c, 1);
	}
	else{
		if(!t[x].el) ls = t[y].l;
		if(!t[x].er)
			t[x].er = 1,
			rs = update(x, t[y].r, mid + 1, r, k, c, 0);
			//右子树同理 
		else 
			rs = update(rs, t[y].r, mid + 1, r, k, c, 1);
	}
	up(x);
	return x;
}

inline int qy(int x, int l, int r, int L, int R){
	if(l >= L and r <= R) return s(x);
	int mid = l + r >> 1, sm = 0;
	if(L <= mid) sm += qy(ls, l, mid, L, R);
	if(R > mid) sm += qy(rs, mid + 1, r, L, R);
	return sm;
}

inline tree ql(int x, int l, int r, int L, int R){
	if(l >= L and r <= R) return t[x];
	int mid = l + r >> 1;
	if(L <= mid and mid < R){
		tree ret, lt, Rt;
		lt = ql(ls, l, mid, L, R);
		Rt = ql(rs, mid + 1, r, L, R);
		ret.sum = lt.sum + Rt.sum;
		ret.lmx = max(lt.lmx, lt.sum + Rt.lmx);
		return ret;
	}
	else if(L <= mid)
		return ql(ls, l, mid, L, R);
	else
		return ql(rs, mid + 1, r, L, R);
}

inline tree qr(int x, int l, int r, int L, int R){
	if(l >= L and r <= R) return t[x];
	int mid = l + r >> 1;
	if(L <= mid and mid < R){
		tree ret, lt, Rt;
		lt = qr(ls, l, mid, L, R);
		Rt = qr(rs, mid + 1, r, L, R);
		ret.sum = lt.sum + Rt.sum;
		ret.rmx = max(Rt.rmx, Rt.sum + lt.rmx);
		return ret;
	}
	else if(L <= mid)
		return qr(ls, l, mid, L, R);
	else
		return qr(rs, mid + 1, r, L, R);
}

inline bool chck(int md){
	int res = 0;
	if(x2 + 1 <= x3 - 1) 
		res += qy(rt[md], 1, n, x2 + 1, x3 - 1);
	res += qr(rt[md], 1, n, x1, x2).rmx;
	res += ql(rt[md], 1, n, x3, x4).lmx;
	return res >= 0;
}

int main(){
	scanf("%d", &n);
	rep(i, 1, n) 
		scanf("%d", &a[i]), num[++tot] = a[i];
	sort(num + 1, num + tot + 1);
	tot = unique(num + 1, num + tot + 1) - num - 1;
	rep(i, 1, n)
		pos[plc(a[i])].push_back(i);
	rt[1] = build(1, n);
	rep(i, 2, tot)	rep(j, 0, (pos[i - 1].size() - 1))
		rt[i] = update(rt[i], rt[i - 1], 1, n, pos[i - 1][j], -1, rt[i] > 0);
	scanf("%d", &m);
	while(m--){
		scanf("%d%d%d%d", &ux[0], &ux[1], &ux[2], &ux[3]);
		rep(i, 0, 3) ux[i] += lst, ux[i] %= n;
		sort(ux, ux + 4);
		x1 = ux[0] + 1, x2 = ux[1] + 1, x3 = ux[2] + 1, x4 = ux[3] + 1;
		int l = 1, r = tot;
		while(l <= r){
			int mid = l + r >> 1;
			if(chck(mid)) ans = mid, l = mid + 1;
			else r = mid - 1;
		}
		printf("%d\n", lst = num[ans]);
	}
	return 0;
}

Problem D


——\(End\)——

标签:比赛,17,int,rep,mid,中位数,num,2022.5,ux
来源: https://www.cnblogs.com/gsn531/p/16496489.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有