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;
}

posted @ 2021-10-03 12:36  siriehn_nx  阅读(65)  评论(0编辑  收藏  举报