[LOJ#2326]「清华集训 2017」简单数据结构

[LOJ#2326]「清华集训 2017」简单数据结构

试题描述

参加完IOI2018之后就是姚班面试。而你,由于讨厌物理、并且想成为乔布斯一样的创业家,被成功踢回贵系。

转眼,时间的指针被指向2019,大二,12月初,考试周。

你早听学长说,数据结构期中考很难,对竞赛生不友好,集训队选手做不完卷子。

你冷笑。哼,堂堂国际金,这点难度的考试算什么。

两小时,你看完习题解析前五章所有内容,并且倒背如流;

一小时,你看了500页的讲义,并且记忆犹新;

十分钟,你骑车到考场,自信的你只带了一把水笔,虽然考试让带资料;

现在,摊开传说中神级卷子,你定神一看——

给出一个长度为 (N) 的序列 (A_{1},A_{2},cdots,A_{N}),如果 (A) 中的一个子序列 (B_1,B_2,cdots,B_M),满足条件:

(1le Mle N)
(forall 1le i<M)(B_i | B_{i+1})

那么称 (B)(A)上升倍数子序列

现在有一个长度为 (N) 的序列 (A) 被初始化为 (A_{1},A_{2},cdots,A_{N}),以及 (Q) 次对序列 (A) 的操作。此处要求实现如下四种操作:

  • 0 x:在序列 (A) 的最左端插入一个数字 (x)
  • 1 x:在序列 (A) 的最右端插入一个数字 (x)
  • 2:移除序列 (A) 最左端的一个数字;
  • 3:移除序列 (A) 最右端的一个数字;

在初始化序列 (A) 和每次操作之后,请计算此时序列 (A) 中最长上升倍数子序列的长度 (mathrm{MaxLen}),以及所有长度为 (mathrm{MaxLen}) 的上升倍数子序列的不同的开头数 (mathrm{Cnt}),输出 (mathrm{MaxLen})(mathrm{Cnt})

为了大幅度降低题目难度,保证在任意时刻序列 (A) 非空,其中的元素互不相等,并且均为 (1sim M) 之间的正整数;同一个数字最多只会被插入 (C) 次。

输入

输入第一行包含三个正整数 (N,M,Q),具体含义见上,保证 (1le N le 10^5)(Nle M le 10^6)(0le Q le 10^5)

输入第二行包含 (N) 个正整数,为 (A_1,A_2,cdots,A_N),保证 (1le A_ile M),并且序列 (A) 中的元素互不相等;

接下来共 (Q) 行输入,每行输入格式形如 0 x 或者 1 x 或者 2 或者 3,具体含义见上。

输出

输出共 (Q+1) 行,在初始化和每次对序列 (A) 操作后,输出 (A) 中最长上升倍数子序列的长度 (mathrm{MaxLen}) 和所有长度为 (mathrm{MaxLen}) 的上升倍数子序列的不同的开头数 (mathrm{Cnt}),用一个空格隔开。

输入示例

5 10 10
1 2 5 9 10
2
1 7
3
3
0 8
3
2
1 8
3
0 3

输出示例

3 1
2 2
2 2
2 2
1 3
1 4
1 3
1 2
2 1
1 2
1 3

数据规模及约定

对于所有的数据,有 (1le N le 10^5)(Nle M le 10^6)(0le Q le 10^5)(1le A_ile M)(C=10)

后记

“奋战两小时,考个四五十”的表情包占领了你的朋友圈:

  • “啊,感觉自己人生完全了”
  • “但愿……我真的能拿到四五十”
  • “我考完了……考完了……完了”
  • “曾经以为是开玩笑的,原来我还是naïve了”

你冷笑。提前半小时交卷,你自然觉得,数据结构,满分,正常。

题解

这题就是一个有时间复杂度保证的暴力。

(f_i) 表示以第 (i) 个数为开头的最长上升倍数子序列长度,然后再记录一下每种 (f_i) 的值各有多少个。同时维护一下对于每个位置 (i),使最长上升倍数子序列长度最长(即取到 (f_i))时可能的所有结尾(我们令位置 (i) 的结尾集合为 (Rely_i))。

左边添加直接维护,就是 dp 再往左推一格。左边删也是只用删掉一个位置的信息。

右边添加 (x),就将所有左边有的 (x) 的约数的位置找出来更新一下,维护一下上面的信息。右边删也是找到所有约数的位置,然后在更新 (f_i) 的时候,讨论一下它的 (Rely_i) 是否只包含最后一个位置,然后当 (f) 值需要变小时暴力处理就好了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
#include <set>
#include <cmath>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 1000010

int n, m, q, f[maxn], pos[maxn], A[maxn], hd, tl, tot[21];
vector <int> divisor[maxn];
set <int> RelyOn[maxn];

void GetDivisor(int x) {
	if(divisor[x].size()) return ;
	int m = (int)sqrt(x + .5);
	rep(i, 1, m) if(x % i == 0) {
		divisor[x].push_back(i);
		if(i != x / i) divisor[x].push_back(x / i);
	}
	sort(divisor[x].begin(), divisor[x].end());
	return ;
}

void upd(int x, int v) {
	if(f[x]) tot[f[x]]--;
	f[x] = v; tot[v]++;
	return ;
}

int tmpos[maxn], cnt;

int main() {
	n = read(); m = read(); q = read();
	hd = q + 1; tl = q;
	rep(i, 1, n) A[++tl] = read(), pos[A[tl]] = tl;
	dwn(i, tl, hd) {
		for(int x = A[i] << 1; x <= m; x += A[i]) if(pos[x] > i) {
			if(f[pos[x]] + 1 > f[i]) upd(i, f[pos[x]] + 1), RelyOn[i] = RelyOn[pos[x]];
			else if(f[pos[x]] + 1 == f[i]) {
				set <int> :: iterator it = RelyOn[pos[x]].begin();
				while(it != RelyOn[pos[x]].end()) {
					if(!RelyOn[i].count(*it)) RelyOn[i].insert(*it);
					it++;
				}
			}
		}
		if(!f[i]) upd(i, 1), RelyOn[i].insert(i);
	}
	
	dwn(i, 20, 1) if(tot[i]){ printf("%d %d
", i, tot[i]); break; }
	rep(kase, 1, q) {
		int tp = read();
//		printf("%d: %d
", kase, tp);
		if(tp == 0) {
			A[--hd] = read(); pos[A[hd]] = hd;
			for(int x = A[hd] << 1; x <= m; x += A[hd]) if(pos[x]) {
				if(f[pos[x]] + 1 > f[hd]) upd(hd, f[pos[x]] + 1), RelyOn[hd] = RelyOn[pos[x]];
				else if(f[pos[x]] + 1 == f[hd]) {
					set <int> :: iterator it = RelyOn[pos[x]].begin();
					while(it != RelyOn[pos[x]].end()) {
						if(!RelyOn[hd].count(*it)) RelyOn[hd].insert(*it);
						it++;
					}
				}
			}
			if(!f[hd]) upd(hd, 1), RelyOn[hd].insert(hd);
		}
		if(tp == 1) {
			A[++tl] = read(); pos[A[tl]] = tl;
			upd(tl, 1); RelyOn[tl].insert(tl);
			GetDivisor(A[tl]);
			cnt = 0;
			rep(i, 0, (int)divisor[A[tl]].size() - 1) {
				tmpos[++cnt] = pos[divisor[A[tl]][i]];
				if(!tmpos[cnt]) cnt--;
			}
			sort(tmpos + 1, tmpos + cnt + 1);
			dwn(i, cnt - 1, 1) rep(j, i + 1, cnt) if(A[tmpos[j]] % A[tmpos[i]] == 0) {
				if(f[tmpos[i]] < f[tmpos[j]] + 1) upd(tmpos[i], f[tmpos[j]] + 1), RelyOn[tmpos[i]] = RelyOn[tmpos[j]];
				else if(f[tmpos[i]] == f[tmpos[j]] + 1) {
					set <int> :: iterator it = RelyOn[tmpos[j]].begin();
					while(it != RelyOn[tmpos[j]].end()) {
						if(!RelyOn[tmpos[i]].count(*it)) RelyOn[tmpos[i]].insert(*it);
						it++;
					}
				}
			}
		}
		if(tp == 2) {
			int x = A[hd++];
			tot[f[pos[x]]]--;
			f[pos[x]] = 0; RelyOn[pos[x]].clear(); pos[x] = 0;
		}
		if(tp == 3) {
			int x = A[tl--], xp = pos[x];
			tot[f[pos[x]]]--;
			f[pos[x]] = 0; RelyOn[pos[x]].clear(); pos[x] = 0;
			GetDivisor(x);
			cnt = 0;
			rep(i, 0, (int)divisor[x].size() - 1) {
				tmpos[++cnt] = pos[divisor[x][i]];
				if(!tmpos[cnt]) cnt--;
			}
			sort(tmpos + 1, tmpos + cnt + 1);
			dwn(i, cnt, 1) {
				int tx = A[tmpos[i]];
				if(!RelyOn[pos[tx]].count(xp)) continue;
				RelyOn[pos[tx]].erase(xp);
				if(RelyOn[pos[tx]].empty()) {
					tot[f[pos[tx]]]--; f[pos[tx]] = 0;
					for(int v = tx << 1; v <= m; v += tx) if(pos[v] > pos[tx]) {
						if(f[pos[v]] + 1 > f[pos[tx]]) upd(pos[tx], f[pos[v]] + 1), RelyOn[pos[tx]] = RelyOn[pos[v]];
						else if(f[pos[v]] + 1 == f[pos[tx]]) {
							set <int> :: iterator it = RelyOn[pos[v]].begin();
							while(it != RelyOn[pos[v]].end()) {
								if(!RelyOn[pos[tx]].count(*it)) RelyOn[pos[tx]].insert(*it);
								it++;
							}
						}
					}
					if(!f[pos[tx]]) upd(pos[tx], 1), RelyOn[pos[tx]].insert(pos[tx]);
				}
			}
		}
//		rep(i, hd, tl) printf("%d%c", A[i], i < tl ? ' ' : '
');
		dwn(i, 20, 1) if(tot[i]){ printf("%d %d
", i, tot[i]); break; }
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8046334.html