J. Joy of Handcraft 2020 China Collegiate Programming Contest - Mianyang Site

Problem - J - Codeforces

题目大意:有n盏灯和m秒,每盏灯会开a[i]秒然后关a[i]秒然后再开a[i]秒,依次循环,灯打开后,就会产生b[i]的亮度值,问m秒内每一秒最大的亮度值是多少

1<=n,m<=1e5;1<=a[i],b[i]<=1e5

思路:对于一盏灯,它打开的时间段是[1,a[i]]、[2*a[i]+1,3*a[i]]...,对于m的时间内,每盏灯最多有m/a[i]/2个区间,我们对于每一个a[i],只保留一个最大的亮度值,这样最多1e5个a[i],枚举区间的复杂度是O(n*logm),然后用线段树维护最大值时间复杂度O(n*logm*logm)

#include <bits/stdc++.h>
//#include<__msvc_all_public_headers.hpp>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
ll a[N];
ll tree[N * 4], tag[N * 4];
int n, m;
void build(int l, int r, int rt)//构造树状数组
{
	tree[rt] = 0;
	tag[rt] = 0;//将所有点的标记初始化为0
	if (l == r)//已经到了最底部
	{
		return;
	}
	int mid = (l + r) >> 1;//将当前区间分成两部分
	build(l, mid, rt << 1);//构建左侧的子树
	build(mid + 1, r, rt << 1 | 1);//构建右侧的子树	
}
void down(int rt, int len)
{//遇到了之前做过标记的点
	if (tag[rt])
	{
		tree[rt << 1] = max(tree[rt << 1], tag[rt]);
		tree[rt << 1 | 1] = max(tree[rt << 1 | 1], tag[rt]);//将子节点更新
		tag[rt << 1] = max(tag[rt << 1], tag[rt]);
		tag[rt << 1 | 1] = max(tag[rt << 1 | 1], tag[rt]);//给两侧的子节点做上同样标记
		tag[rt] = 0;
	}
}
void add(int a, int b, ll c, int l, int r, int rt)
{//a到b内每一个元素都+c
	if (a <= l && b >= r)
	{//当前区间已经包含在要查询的区间内
		tree[rt] = max(c, tag[rt]);
		tag[rt] = max(c, tag[rt]);
		return;
	}
	down(rt, r - l + 1);
	int mid = (l + r) >> 1;
	if (a <= mid)
	{
		add(a, b, c, l, mid, rt << 1);
	}
	if (b > mid)
	{
		add(a, b, c, mid + 1, r, rt << 1 | 1);
	}
	tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];//回溯时改变父结点的值
}

long long query(int a, int b, int l, int r, int rt)
{
	if (a <= l && b >= r)//如果当前的区间在要查询的区间内,就加上这部分结果
		return tree[rt];
	down(rt, r - l + 1);
	int mid = (l + r) >> 1;
	ll ans = 0;
	if (a <= mid)
		ans = max(ans,query(a, b, l, mid, rt << 1));//维护最大值
	if (b > mid)
	{
		ans = max(ans,query(a, b, mid + 1, r, rt << 1 | 1));
	}
	return ans;
}
void init()
{
	for (int i = 1; i <= 100000; i++)
	{
		a[i] = 0;
	}
}
void solve(int t)
{
	cin >> n >> m;
	init();
	for (int i = 1; i <= n; i++)
	{
		ll x,y;
		cin >> x >> y;
		a[x] = max(a[x], y);
	}
	build(1, m, 1);
	for (ll i = 1; i <= 100000; i++)
	{
		if (!a[i])
		{
			continue;
		}
		ll now = 1;
		while (i * now - i + 1 <= m)
		{//枚举所有打开的区间
			ll l = i * now - i + 1, r = i * now;
			add(l, r, a[i], 1, m, 1);
			now += 2;
		}
	}
	cout << "Case #" << t << ": ";
	for (int i = 1; i <= m; i++)
	{
		if (i == m)
		{
			cout << query(i, i, 1, m, 1);
			break;
		}
		cout << query(i, i, 1, m, 1) << " ";
	}
	cout << '\n';
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin >> t;
	int cnt = 0;
	while (t--)
	{
		solve(++cnt);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值