2020CCPC-Mianyang Station-Joy of Handcraft(ラインセグメントツリーとファクター分割)

リトルホースはいつもいくつかの手工芸品を作っていますが、それは喜びに満ちています。今回、彼は定期的に電球をオン/オフできる回路を構築します。

回路にはn個の電球があり、そのi番目の電球の周期はtiで輝度はxiです。正式には、i番目の電球は(2kt i +1)秒から(2kt i + t i)秒までオンになり、オフになります(2kt i + t i +1)-秒から(2kt i + 2t i)-秒まで、k = 0,1,2、.. .. i番目の電球がオンの場合、その輝度はx iになります。それ以外の場合、その輝度は0になります。

さて、リトルホースは、最初の1秒からm番目の秒までの毎秒、すべての電球の最大輝度を知りたいと思っています。

入力

入力の最初の行には、整数T(1≤T≤100)-テストケースの数が含まれています。

各テストケースの最初の行には、2つの整数n、m(1≤n、m≤105)-電球の数と、出力する必要のある整数の数が含まれています。nの合計とmの合計は2×105を超えません。

次に、次のn行で、i番目の行に2つの整数t i、x i(1≤ti、xi≤105)-期間が含まれますそしてi番目の電球の輝度。

出力

x番目のテストケースはケース#x:で始まり、その後にm個の整数が続きます。i番目の整数は、i番目の秒におけるすべての電球の最大輝度を示します。i番目の秒に電球がオンになっていない場合は、0を出力します。

サンプル入力

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

サンプル出力

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

本旨:

N個の電球があり、各電球には2つのプロパティがあります。Ti、Xi、Xiは電球の明るさを表し、Tiは切り替えサイクルを表し、電球がオンになるまでの時間は(2ktTi +1)秒です。 (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の場合、最大(3 * M、3 *最大電球周期)を直接取得することに注意してください。 )。

受け入れられたコード

#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