ABC393 调和级数 LIS+离线

E

调和级数

对一个数列,问每个aia_iai,选kkk个数,包含aia_iai,最大的gcdgcdgcd是多少

注意到gcdgcdgcd有一个性质,他一定是所有数的因子,并且还是最大的那个。所以这种至少是kkk个数的最大gcdgcdgcd的问题,一种做法是,枚举答案,然后枚举倍数,检查他的倍数是否至少有kkk个。如果是,那么这个枚举的数,就一定是至少kkk个数的因数,并且可能是这些数的gcdgcdgcd。要求gcdgcdgcd的话,仍然可以这样枚举下去,因为最大的gcdgcdgcd一定会被我们枚举到。

然后这里进阶一点的问题是,多次询问,每次问包含aia_iaikkk个数的最大gcdgcdgcd。仍然可以上述的调和级数枚举,然后对于一个答案xxx,如果他是合法的,即倍数超过kkk个,那么他的所有倍数的答案都可以是xxx,用xxx更新所有倍数的答案,更新是取maxmaxmax

这里我们就记录了所有值的答案,输出答案时根据aia_iai的值查表即可

#include <bits/stdc++.h>
using namespace std;
void solve() {
	int n, k;
	cin >> n >> k;

	vector<int>a(n);
	int mx = 0;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
		mx = max(mx, a[i]);
	}

	vector<int>b(mx + 1);
	for (int x : a) {
		b[x]++;
	}


	vector<int>ans(mx + 1);
	for (int i = 1; i <= mx; i++) {
		int cnt = 0;
		for (int j = i; j <= mx; j += i) {
			cnt += b[j];
		}
		if (cnt >= k) {
			for (int j = i; j <= mx; j += i) {
				ans[j] = i;
			}
		}
	}
	for (int x : a) {
		cout << ans[x] << '\n';
	}
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	int T = 1;
//	cin>>T;
	while (T--) {
		solve();
	}
	return 0;
}

F

线段树 LIS 离线

多次询问,每次问只用前缀[1,ri][1,r_i][1,ri]里的元素,能得到的,最大值不超过xix_ixi的严格LISLISLIS的长度。

注意到LISLISLIS里最大元素就是结尾元素,所以问的就是结尾元素不超过xix_ixiLISLISLIS。然后每次询问不一定是对整个序列的dpdpdp结果来说的,可能是对某个前缀的dpdpdp结果来说的。那么就把询问离线,在dpdpdp到一个前缀[1,i][1,i][1,i]时,回答r=ir=ir=i的所有询问。

实现上就是一个线段树优化LISLISLIS,回答询问也是一个类似LISLISLIS的转移,查询结尾元素在[1,xi][1,x_i][1,xi]内的最长LISLISLIS线段树查询即可。

ai,xia_i,x_iai,xi很大,我们要用线段树维护值域,则需要先离散化。

#include <bits/stdc++.h>
#define ls u<<1
#define rs u<<1|1
using namespace std;

const int N = 4e5 + 10;
struct tree {
	struct node {
		int l, r, mx;
	} tr[N << 2];

	void pushup(int u) {
		tr[u].mx = max(tr[ls].mx, tr[rs].mx);
	}
	void build(int u, int l, int r) {
		tr[u] = {l, r, 0};
		if (l == r) {
			return;
		}
		int m = l + r >> 1;
		build(ls, l, m);
		build(rs, m + 1, r);
	}

	void upd(int u, int idx, int v) {
		if (tr[u].l == tr[u].r) {
			tr[u].mx = max(tr[u].mx, v);
			return;
		}

		int m = (tr[u].l + tr[u].r) / 2;
		if (idx <= m) {
			upd(ls, idx, v);
		} else {
			upd(rs, idx, v);
		}
		pushup(u);
	}

	int ask(int u, int l, int r) {
		if (l <= tr[u].l && tr[u].r <= r) {
			return tr[u].mx;
		}
		int m = (tr[u].l + tr[u].r) / 2;
		if (l > m) {
			return ask(rs, l, r);
		} else if (r <= m) {
			return ask(ls, l, r);
		}
		return max(ask(ls, l, r), ask(rs, l, r));
	}

} t;
void solve() {
	int n, q;
	cin >> n >> q;

	vector<int>all;
	vector<int>a(n);
	for (int &x : a) {
		cin >> x;
		all.push_back(x);
	}

	vector<int>r(q), x(q);
	for (int i = 0; i < q; i++) {
		cin >> r[i] >> x[i];
		all.push_back(x[i]);
	}

	sort(all.begin(), all.end());
	all.erase(unique(all.begin(), all.end()), all.end());

	for (int i = 0; i < n; i++) {
		a[i] = lower_bound(all.begin(), all.end(), a[i]) - all.begin() + 1;
	}

	for (int i = 0; i < q; i++) {
		x[i] = lower_bound(all.begin(), all.end(), x[i]) - all.begin() + 1;
	}

	vector<vector<pair<int, int>>>Q(n);
	for (int i = 0; i < q; i++) {
		Q[r[i] - 1].emplace_back(x[i], i);
	}
	vector<int>ans(q);

	t.build(1, 0, all.size());
	for (int i = 0; i < n; i++) {
		int mx = t.ask(1, 0, a[i] - 1);
//		cout << a[i] << ':';
		t.upd(1, a[i], mx + 1);
//		cout <<  mx + 1 << '\n';

		for (auto [x, id] : Q[i]) {
			ans[id] = t.ask(1, 0, x);
		}
	}
	for (int x : ans) {
		cout << x << '\n';
	}
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	int T = 1;
//	cin>>T;
	while (T--) {
		solve();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值