CLYZ-NOIP十连测 Day4
CLYZ-NOIP2021 国庆集训 B 组 Day4
题面:https://files.cnblogs.com/files/blogs/575626/day4B.zip
偏序问题
这玩意,出题人的双亲大抵是溘然长逝的吧。
这玩意放T1
我们考虑已经知道的某些求解高位偏序问题的方法。
首先你可以考虑 \(\text{k-d Tree}\) ,不过这玩意单次操作复杂度 \(O(n^{\frac{k-1}{k}})\) ,所以你的 kd树其实在精心构造的数据下面跑不过暴力。
其次考虑 cdq 分治,这玩意你需要嵌套 \(k\) 次,复杂度为 \(O(n\log^k n)\) ,\(n\) 不大的时候甚至不如暴力。
我们还有一个办法,就是暴力了。考虑用 bitset
来存下来每个数字每一维的大小关系,然后这个东西,然后对于每个 \(i\) ,只要把 \(k\) 维的 bitset
与起来就是答案了。
这个方法时间复杂度为 \(O(\frac{1}{w}n^2k)\),空间复杂度为 \(O(\frac{1}{w}n^2k)\)。
这玩意,空间不太行。我们考虑怎么搞空间,于是就需要优美的分块来拯救世界了。
我们对于每一维东西,只存每个根号位置的比它小的位置。然后每次用的时候在用散块 \(O(\sqrt n+\frac{1}{w}n)\) 的查询。
然后空间复杂度就变成了 \(O(\frac{1}{w}n^{\frac{3}{2}}k)\)。 大概就可以跑了。
#include <bits/stdc++.h>
using std::bitset;
using std::cin;
using std::cout;
using std::make_pair;
using std::pair;
const int N = 1e5 + 10;
bitset<N> qs[12][350];
pair<int, int> val[12][N];
int n, blk_sz, bl[N], L[N], R[N], f[12][N], K, num;
long long ret;
int find(int k, int x) {
int l = 1, r = n, ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
val[k][mid].first <= x ? ans = x, l = mid + 1 : r = mid - 1;
}
return ans;
}
bitset<N> getbst(int p, int x) {
bitset<N> ans;
ans.reset();
int pos = find(p, x);
if (pos <= 0) {
return ans;
}
int pre = (pos - 1) / blk_sz, st = pre * blk_sz + 1;
ans = qs[p][pre];
for (int i = st; i <= pos; ++i) {
ans.set(val[p][i].second);
}
return ans;
}
int main() {
freopen("partial_order.in", "r", stdin);
freopen("partial_order.out", "w", stdout);
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cin >> K >> n;
++K;
blk_sz = sqrt(n);
for (int i = 1; i <= n; ++i) {
val[1][i] = make_pair(f[1][i] = i, i);
}
for (int j = 2; j <= K; ++j) {
for (int i = 1; i <= n; ++i) {
std::cin >> f[j][i];
val[j][i] = make_pair(f[j][i], i);
}
}
for (int i = 1; i <= K; ++i) {
sort(&val[i][1], &val[i][n + 1]);
}
for (int i = 1; i <= n; ++i) {
bl[i] = (i - 1) / blk_sz + 1;
}
num = bl[n];
for (int i = 1; i <= num; ++i) {
L[i] = (i - 1) * blk_sz + 1, R[i] = i * blk_sz;
}
R[num] = n;
for (int j = 1; j <= K; ++j) {
for (int i = 1; i <= num; ++i) {
qs[j][i] = qs[j][i - 1];
for (int k = L[i]; k <= R[i]; ++k) {
qs[j][i][val[j][k].second] = 1;
}
}
}
bitset<N> ans;
ans.reset();
for (int i = 1; i <= n; ++i) {
ans.set();
for (int j = 1; j <= K; ++j) {
ans &= getbst(j, f[j][i]);
}
ret += ans.count() - 1;
// erase itself
}
cout << ret << '\n';
return 0;
}
不同子串
卡单哈希的出题人多少沾点吧。
我们直接双哈希即可。
#include <bits/stdc++.h>
const int N = 2e5 + 10;
using std::cin;
using std::cout;
using std::pair;
using std::make_pair;
using std::map;
const unsigned long long mod = 1222827239, base = 23, base2 = 11, mod2 = 998244353;
unsigned long long pw[N], qs[N], qs2[N], pw2[N];
std::map<std::pair<unsigned long long, unsigned long long>, int> mp;
int n, m;
char c[N];
inline pair<unsigned long long, unsigned long long> query(int x, int y) {
return make_pair((qs[y] - qs[x - 1] * pw[y - x + 1] % mod + mod) % mod, (qs2[y] - qs2[x - 1] * pw2[y - x + 1] % mod2 + mod2) % mod2);
}
int main() {
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
std::ios::sync_with_stdio(0);
std::cin.tie(0);
cin >> n >> m;
cin >> c + 1;
pw[0] = 1;
pw2[0] = 1;
for (int i = 1; i <= n; ++i) {
pw[i] = pw[i - 1] * base % mod;
pw2[i] = pw2[i - 1] * base2 % mod2;
}
for (int i = 1; i <= n; ++i) {
qs[i] = (qs[i - 1] * base + c[i]) % mod;
qs2[i] = (qs2[i - 1] * base2 + c[i]) % mod2;
}
int ans = 0;
for (int i = m; i <= n; ++i) {
if (!mp.count(query(i - m + 1, i))) {
++ans;
mp[query(i - m + 1, i)] = 1;
}
}
cout << ans << '\n';
return 0;
}
排队问题
我们考虑直接贪心处理,首先从小到大排序,然后搞一个什么每次放在左边有 \(b_i+1\) 个空位或者是右边有 \(k_i+1\) 个空位的位置即可。
然后放不了就是无解。
#include <bits/stdc++.h>
const int N = 2e5 + 10;
struct rec {
int a, b;
} qs[N];
int w[1 << 19], bas = 1, n, ans[N];
inline int ls(int k) {
return k << 1;
}
inline int rs(int k) {
return k << 1 | 1;
}
int query(int k, int l, int r, int x) {
if (l == r) {
return l;
}
int mid = (l + r) >> 1;
return x <= w[ls(k)] ? query(ls(k), l, mid, x) : query(rs(k), mid + 1, r, x - w[ls(k)]);
}
int getf(int k, int l, int r, int x)
{
if (l == r)
return l;
int mid = (l + r) >> 1;
if (x <= w[k << 1])
return getf(k << 1, l, mid, x);
else
return getf(k << 1 | 1, mid + 1, r, x - w[k << 1]);
}
bool cmp(rec x, rec y) { return x.a < y.a; }
int main() {
freopen("queue.in", "r", stdin);
freopen("queue.out", "w", stdout);
scanf("%d", &n);
while ((bas <<= 1) < n)
;
for (int i = 1; i <= n; ++i) {
scanf("%d %d", &qs[i].a, &qs[i].b);
}
std::sort(qs + 1, qs + 1 + n, cmp);
int flag = 0;
for (int i = 0; i < n; ++i) {
w[bas + i] = 1;
}
for (int i = bas - 1; i; --i) {
w[i] = w[ls(i)] + w[rs(i)];
}
for (int i = 1; i <= n; ++i) {
int pos, j, now = std::min(qs[i].b, n - i - qs[i].b) + 1;
if (now <= 0 || now > w[1]) {
flag = 1;
break;
}
ans[pos = getf(1, 1, bas, now)] = qs[i].a;
for (w[j = bas + pos - 1] = 0, j >>= 1; j; j >>= 1) {
w[j] = w[ls(j)] + w[rs(j)];
}
}
if (flag) {
printf("impossible");
return 0;
}
for (int i = 1; i <= n; ++i) {
printf("%d ", ans[i]);
}
puts("");
return 0;
}