题目地址:P5270 无论怎样神树大人都会删库跑路
第一眼看上去是模拟,似乎是 \(O(n)\) 的
水题
信心满满的写完:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 6;
int n, T, Q, m, c[N], r[N], mx, cnt, d[N], ans, s[N], now;
vector<int> e[N];
queue<int> q;
int main() {
cin >> n >> T >> Q;
for (int i = 1; i <= T; i++) {
int x;
scanf("%d", &x);
mx = max(mx, x);
++c[x];
}
for (int i = 1; i <= n; i++) {
int len;
scanf("%d", &len);
while (len--) {
int x;
scanf("%d", &x);
mx = max(mx, x);
e[i].push_back(x);
}
}
cin >> m;
for (int i = 1; i <= m; i++) scanf("%d", &r[i]);
for (int x = 0; x <= mx; x++) if (c[x] == d[x]) ++cnt;
for (int i = 1; i <= m && i <= Q; i++) {
for (unsigned int j = 0; j < e[r[i]].size(); j++) {
int x = e[r[i]][j];
q.push(x);
if (d[x] == c[x]) --cnt;
++d[x];
if (d[x] == c[x]) ++cnt;
}
while ((int)q.size() > T) {
int x = q.front();
q.pop();
if (d[x] == c[x]) --cnt;
--d[x];
if (d[x] == c[x]) ++cnt;
}
if (cnt == mx + 1) ++ans;
}
if (Q <= m) {
cout << ans << endl;
return 0;
}
Q -= m;
for (int i = 1; i <= m; i++) {
for (unsigned int j = 0; j < e[r[i]].size(); j++) {
int x = e[r[i]][j];
q.push(x);
if (d[x] == c[x]) --cnt;
++d[x];
if (d[x] == c[x]) ++cnt;
}
while ((int)q.size() > T) {
int x = q.front();
q.pop();
if (d[x] == c[x]) --cnt;
--d[x];
if (d[x] == c[x]) ++cnt;
}
if (cnt == mx + 1) ++now;
s[i] = now;
}
cout << ans + now * (Q / m) + s[Q%m] << endl;
return 0;
}
Wait!
Q:为什么在模拟完第一遍后要再模拟一遍呢?
A:第一遍初始时没有数,而后面会有剩下的数留到下一轮,因此第一遍对 \(ans\) 的贡献要特判QAQ
一遍AC
然而......
模拟是 \(O(n)\) 的?
抱歉,假了,50分滚粗
康康这组数据:
1 1e5 1e9
1 1 ... 1
1e5 1 1 ... 1
1e5
1 1 ... 1
你会发现
被卡成 \(O(n^2)\) 了!
Q:怎么办?
A:骂毒瘤出题人
我们发现瓶颈在这:
for (unsigned int j = 0; j < e[r[i]].size(); j++) {
int x = e[r[i]][j];
q.push(x);
if (d[x] == c[x]) --cnt;
++d[x];
if (d[x] == c[x]) ++cnt;
}
while ((int)q.size() > T) {
int x = q.front();
q.pop();
if (d[x] == c[x]) --cnt;
--d[x];
if (d[x] == c[x]) ++cnt;
}
这个插入删除是 \(O(n)\) 的!
Q:我们能不能把它搞成 \(O(1)\) 的?
A:可以
考虑 Hash 思想
先看代码:
#include <bits/stdc++.h>
#define ull unsigned long long
#define pii pair<int, int>
using namespace std;
const int N = 1e5 + 6;
int n, T, Q, S[N], R[N], m, mx, len, ans, s[N], kkk;
vector<int> e[N];
struct H {
ull h[6];
H() {
memset(h, 0, sizeof(h));
}
} Hash[N], numS, now;
vector<H> num[N];
deque<pii> q;
inline ull RA() {
return (ull)rand() * rand() * rand() * rand() * rand() * rand();
}
inline void add(H &a, H &b) {
for (int i = 0; i < 6; i++) a.h[i] += b.h[i];
}
inline void del(H &a, H &b) {
for (int i = 0; i < 6; i++) a.h[i] -= b.h[i];
}
inline bool equ(H a, H b) {
for (int i = 0; i < 6; i++)
if (a.h[i] != b.h[i]) return 0;
return 1;
}
int main() {
srand((unsigned)time(0));
cin >> n >> T >> Q;
for (int i = 1; i <= T; i++) {
scanf("%d", &S[i]);
mx = max(mx, S[i]);
}
for (int i = 1; i <= n; i++) {
int len;
scanf("%d", &len);
while (len--) {
int x;
scanf("%d", &x);
mx = max(mx, x);
e[i].push_back(x);
}
}
cin >> m;
for (int i = 1; i <= m; i++) scanf("%d", &R[i]);
for (int i = 0; i <= mx; i++)
for (int j = 0; j < 6; j++)
Hash[i].h[j] = RA();
for (int i = 1; i <= T; i++) add(numS, Hash[S[i]]);
for (int i = 1; i <= n; i++) {
int len = e[i].size();
num[i].resize(len);
for (int j = len - 1; ~j; j--)
add(num[i][j], Hash[e[i][j]]);
for (int j = len - 2; ~j; j--)
add(num[i][j], num[i][j+1]);
}
for (int i = 1; i <= m && i <= Q; i++) {
add(now, num[R[i]][0]);
len += e[R[i]].size();
q.push_back({R[i], 0});
while (len > T) {
pii x = q.front();
q.pop_front();
del(now, num[x.first][x.second]);
len -= e[x.first].size() - x.second;
if (len < T) {
int k = num[x.first].size() - (T - len);
q.push_front({x.first, k});
add(now, num[x.first][k]);
len = T;
}
}
if (equ(now, numS)) ++ans;
}
if (Q <= m) {
cout << ans << endl;
return 0;
}
Q -= m;
for (int i = 1; i <= m && i <= Q; i++) {
add(now, num[R[i]][0]);
len += e[R[i]].size();
q.push_back({R[i], 0});
while (len > T) {
pii x = q.front();
q.pop_front();
del(now, num[x.first][x.second]);
len -= e[x.first].size() - x.second;
if (len < T) {
int k = num[x.first].size() - (T - len);
q.push_front({x.first, k});
add(now, num[x.first][k]);
len = T;
}
}
if (equ(now, numS)) ++kkk;
s[i] = kkk;
}
cout << ans + kkk * (Q / m) + s[Q%m] << endl;
return 0;
}
(小声)不好意思kkk,窝真的是没有变量名可用才用您的QWQ
我们给每个数若干维随机 Hash 值
for (int i = 0; i <= mx; i++)
for (int j = 0; j < 6; j++)
Hash[i].h[j] = RA();
一串数的 Hash 值定义为每个数的每一维 Hash 分别相加(unsigned long long自然溢出)
如果两个串任意排列后可以相同,这两个串对应的每一维都应该相同
那么插入和删除就都可以做到 \(O(1)\) 了
你卡我随机化鸭
你卡我 Hash 鸭
你卡我常数大鸭