首页 > 其他分享 >【YBT2023寒假Day12 A】我的世界(二分)(主席树)

【YBT2023寒假Day12 A】我的世界(二分)(主席树)

时间:2023-02-22 09:36:24浏览次数:43  
标签:二分 lceil ll YBT2023 mid 寒假 Day12

我的世界

题目链接:YBT2023寒假Day12 A

题目大意

有 n 个数,每一秒每个数都会减小 1,而且你可以选一个数让它减小 x,小于 0 的数会变成 0。
给你 s 秒,问你 s 秒操作后所有数中最大的数的最小值是多少。

思路

首先我们考虑二分这个答案,然后考虑能不能达到。
然后发现需要的时间是 \(\sum\limits_{i=1}^n\left\lceil\dfrac{\max\{0,t_i-s-mid\}}{x}\right\rceil\)。
首先去掉 \(\max\),二分找到第一个 \(t_i-s-mid>0\) 的 \(i\)。

然后处理上取整,考虑 \(\left\lceil t_i/x\right\rceil\) 对于每个数固定,预处理出它的前缀和。
然后减去 \(\left\lceil\dfrac{s+mid}{x}\right\rceil\),然后考虑错了的部分。
不难发现错了的原因会是 \(t_i\%x>(s+mid)\%x\),那我们可以开一个值域 \(x\) 的动态开点可持续化线段树,维护每一个前缀(我这里把数从大到小排序所以是前缀)余数为 \(l \sim r\) 的 \(t_i\) 的个数。
然后加上这个个数就行(每次错都会小了 \(1\))

代码

#include<cstdio>
#include<algorithm>
#define ll long long

using namespace std;

const ll N = 1e5 + 100;
ll n, m, x, t[N], rt[N], sum[N];

bool cmp(ll x, ll y) {
	return x > y;
}

struct XD_tree {
	ll f[N << 7], ls[N << 7], rs[N << 7], tot;
	
	ll insert(ll nw, ll l, ll r, ll pl) {
		ll now = ++tot; f[now] = f[nw]; ls[now] = ls[nw]; rs[now] = rs[nw];
		f[now]++;
		if (l == r) return now;
		ll mid = (l + r) >> 1;
		if (pl <= mid) ls[now] = insert(ls[now], l, mid, pl);
			else rs[now] = insert(rs[now], mid + 1, r, pl);
		return now;
	}
	
	ll query(ll now, ll l, ll r, ll L, ll R) {
		if (!now) return 0;
		if (L > R) return 0;
		if (L <= l && r <= R) return f[now];
		ll mid = (l + r) >> 1, re = 0;
		if (L <= mid) re += query(ls[now], l, mid, L, R);
		if (mid < R) re += query(rs[now], mid + 1, r, L, R);
		return re;
	}
}T;

ll check(ll TT) {
	ll l = 1, r = n, re = n + 1;
	while (l <= r) {
		ll mid = (l + r) >> 1;
		if (t[mid] <= TT) re = mid, r = mid - 1;
			else l = mid + 1;
	}
	ll num = sum[re - 1] - (re - 1) * (TT / x);
	ll lim = TT % x;
	ll cnt = T.query(rt[re - 1], 0, x - 1, lim + 1, x - 1);
	num += cnt;
	return num;
}

int main() {
	freopen("bone.in", "r", stdin);
	freopen("bone.out", "w", stdout);
	
//	printf("%.3lf", (sizeof(T) + sizeof(t) * 10) / 1024.0 / 1024.0);
	
	scanf("%lld %lld %lld", &n, &m, &x);
	for (ll i = 1; i <= n; i++) scanf("%lld", &t[i]);
	sort(t + 1, t + n + 1, cmp);
	for (ll i = 1; i <= n; i++) sum[i] = sum[i - 1] + (t[i] / x);
	
	for (ll i = 1; i <= n; i++) rt[i] = T.insert(rt[i - 1], 0, x - 1, t[i] % x);
	while (m--) {
		ll s; scanf("%lld", &s);
		ll L = 0, R = 1e18, re = 1e18;
		while (L <= R) {
			ll mid = (L + R) >> 1;
			if (check(mid + s) <= s) re = mid, R = mid - 1;
				else L = mid + 1;
		}
		printf("%lld\n", re);
	}
	
	return 0;
}

标签:二分,lceil,ll,YBT2023,mid,寒假,Day12
From: https://www.cnblogs.com/Sakura-TJH/p/YBT2023Day12_A.html

相关文章

  • 【YBT2023寒假Day11 B】催眠大师(费用流)
    催眠大师题目链接:YBT2023寒假Day11B题目大意有一个n*n的棋盘,有一些位置有障碍。然后定义棋盘上两个位置能相互攻击当且仅当在同一行或同一列,且之间的所有位置都没有......
  • 【YBT2023寒假Day11 A】海妖沙龙(计算几何)
    海妖沙龙题目链接:YBT2023寒假Day11A题目大意平面上有n个点,然后对于一个排列,如果按顺序走对于的点,会形成若干个线段组成的路径,然后你从前一个线段走到下一个线段端点......
  • H - 线段树 1【GDUT_22级寒假训练专题五】
    H-线段树1原题链接题意区间修改区间查询线段树简介线段树是一颗二叉树,他能通过树的分支将一块区间划分为数个单元区间,每个单元区间对应线段树中的一个叶结点。如......
  • 【YBT2023寒假Day10 C】娄居吉勾(点分树)
    娄居吉勾题目链接:YBT2023寒假Day10C题目大意有一个n个点m条边的无向连通图,每个点至多在k个简单环上。然后有q个操作,标记一个没有标记过的点,或者给你一个点求......
  • 寒假训练第四周
      题目大意意思就是给一个n和一个kk是2的k次方n是分子在2的k次方上面从1到n分子依次加一然后要进行分数有理化然后把有理化的分子相加即可思路因为是2的k次方......
  • 寒假学习记录(三)
    这个作业属于哪个课程<班级的链接>这个作业要求在哪里<作业要求的链接>这个作业的目标学有所得一、写在前面对于每次寒假作业的目标,我给自己设定的都......
  • 【YBT2023寒假Day10 B】随机游走(记忆化搜索)
    随机游走题目链接:YBT2023寒假Day10B题目大意有n个点排成环,你一开始在1号点,每次可以等概率选择左边跳两格,左边跳一格,右边跳一格,右边跳两格。走到一个走过的点就停......
  • 【YBT2023寒假Day10 A】集合比较(数学)(启发式分裂)
    集合比较题目链接:YBT2023寒假Day10A题目大意给你一个长度为n的排列p,定义两个大小为n不可重集合的比较方式是先比较各自第p1小的元素,如果相同比p2,以此类推。给......
  • 寒假学习总结
    这个作业属于哪个课程班级这个作业要求在哪里作业的要求这个作业的目标对寒假学习的总结寒假学习总结一、回顾在本次寒假的学习当中,收益颇丰,每次作业......
  • 寒假训练第六周
    寒假训练第六周第一天蓝桥杯训练斐波拉契数组大意:给定数组A,为最少修改几个元素后,数组A会变成一个斐波拉契数组。思路:斐波那契数组在第 30 项左右就超过 1e6了。......