2020CCPC-绵阳站-Joy of Handcraft (线段树&因子拆分)

Little Horse always does some handcrafts, which is full of joy. This time, he builds a circuit that can turn on and off the bulbs periodically.

There are n bulbs in the circuit, the i-th of which has a period t​i​​ and a luminance x​i​​. Formally, the i-th bulb will be turned on from the (2kt​i​​+1)-th second to the (2kt​i​​+t​i​​)-th second, and it will be turned off from the (2kt​i​​+t​i​​+1)-th second to the (2kt​i​​+2t​i​​)-th second, k=0,1,2,... When the i-th bulb is on, its luminance will be x​i​​, otherwise its luminance will be 0.

Now, Little Horse wants to know, for each second from the first second to the m-th second, what's the maximum luminance among all the bulbs.

Input

The first line of the input contains an integer T (1≤T≤100) − the number of test cases.

The first line of each test case contains two integers n,m (1≤n,m≤10​5​​) − the number of bulbs, and the number of integers you need to output. The sum of n and the sum of m will not exceed 2×10​5​​.

Then in the next n lines, the i-th line contains two integers t​i​​,x​i​​ (1≤t​i​​,x​i​​≤10​5​​) − the period and the luminance of the i-th bulb.

Output

The x-th test case begins with Case #x:, and there follow m integers. The i-th integer indicates the maximum luminance among all the bulbs in the i-th second. If no bulb is on in the i-th second, output 0.

Sample Input

3
2 3
1 1
2 2
2 5
1 2
2 3
3 3
1 1
1 2
1 3

Sample Output

Case #1: 2 2 1
Case #2: 3 3 2 0 3
Case #3: 3 0 3

题目大意:

有N个灯泡,每个灯泡有两种属性分别为Ti,Xi,Xi表示的是该灯泡的亮度,Ti表示的是开关周期,一个灯泡打开的时间为 (2ktTi​​+1)-th second to the (2ktTi​​+T​i​​),k=0,1,2,...  现在问你从1到M的时间,每单位时间的最大亮度为多少。

解法:

不难想到要用线段树来维护最大值,初始想法是对于每个灯泡,以他的亮周期进行更新,但是这样复杂度肯定过不去,下面就看怎么优化。

观察一下灯泡的亮周期,可以发现亮周期的右端点为T,3 * T,5 * T...T前面的系数是依次递增的奇数,而且一旦确定了右端点那么区间也能够确定,根据这个性质,可以列出等式 S * T = W,其中S表示的就是T前面的系数,W就是枚举的时间(从1到M),对于W求一下因子,如果是奇数,那么就可以找到相应具有时间周期T的灯泡,再进行区间修改最大值即可。

需要注意的是对于每组样例,线段树最右端点的确定,因为可能存在某个灯泡的右端点 > M,但是左端点 < M,我直接取得max(3 * M, 3 * 最大灯泡周期)。

Accepted code

#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;

#define sc scanf
#define Max(a, b) a = max(a, b)
typedef long long ll;
const int N = 5e5 + 100;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;

int sum[N], mxt[N * 4];
int t[N], x[N];
int n, m;

#define ls o << 1
#define rs ls | 1
void Build(int o, int L, int R) {
	mxt[o] = 0;
	if (L == R)
		return;
	int mid = (L + R) >> 1;
	Build(ls, L, mid);
	Build(rs, mid + 1, R);
}
void Pushdown(int o) {
	Max(mxt[ls], mxt[o]), Max(mxt[rs], mxt[o]);
}
void Add(int o, int L, int R, int l, int r, int k) {
	if (L >= l && R <= r)
		Max(mxt[o], k);
	else {
		Pushdown(o);
		int mid = (L + R) >> 1;
		if (mid >= l)
			Add(ls, L, mid, l, r, k);
		if (mid < r)
			Add(rs, mid + 1, R, l, r, k);
	}
}
int Ask(int o, int L, int R, int x) {
	if (L == R)
		return mxt[o];
	else {
		Pushdown(o);
		int mid = (L + R) >> 1;
		int tot = 0;
		if (mid >= x)
			Max(tot, Ask(ls, L, mid, x));
		else
			Max(tot, Ask(rs, mid + 1, R, x));
		return tot;
	}
}

int main()
{
	int Cas = 0;
	int T; cin >> T;
	while (T--) {
		sc("%d %d", &n, &m);

		int mx = 0;
		for (int i = 1; i <= n; i++) {
			sc("%d %d", &t[i], &x[i]);
			Max(sum[t[i]], x[i]);
			Max(mx, t[i]);
		}

		int ri = max(3 * mx, 3 * m);  // 最右端点
		for (int i = 1; i <= ri; i += 2) {
			for (int j = i; j <= ri; j += i)
			{
				if (i & 1) {  // i是j的奇数因子
					int tmp = j / i; // 时间
					if (!sum[tmp])
						continue;
					int r = j, l = j - tmp + 1;  // 区间
					Add(1, 1, ri, l, r, sum[tmp]);  // 区间标记最值
				}
			}
		}
		printf("Case #%d: ", ++Cas);
		for (int i = 1; i <= m; i++) {
			printf("%d", Ask(1, 1, ri, i));
			if (i == m)
				puts("");
			else
				printf(" ");
		}
		for (int i = 1; i <= n; i++)  // 清空
			sum[t[i]] = 0;
		Build(1, 1, ri);   // 线段树清空
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43851525/article/details/109436883