数据结构做题记录
本人由于数据结构太菜,所以有了这个东西。
T1 互斥的数
分析题目,求两两不互斥的最大子集。换句话说,就是删除互斥的元素。
考虑贪心,将数组从小到大排序。
如果 \(a_i\) 与 \(a_j\) 是互斥的,这两个数当中必须要删掉一个。显然删掉 \(a_j\) 是更划算的,因为 \(a_i\) 只是影响到了 \(a_j\) ,而 \(a_j\) 还会影响 \(p × a_j\) 。
由于 \(a_i\) 的值很大,所以我们用 \(\texttt{hash}\) 维护即可。
#include <bits/stdc++.h>
using namespace std;
const int P = 1e8 + 7, p = 31;
int n, k;
int a[100005], ans;
bool b[P + 5];
int get_hash(int x) {
return (p + x) % P;
}
signed main() {
scanf("%d %d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) {
if (!b[get_hash(a[i])]) {
ans++;
b[get_hash(a[i] * k)] = true;
}
}
printf("%d", ans);
return 0;
}
T2 Power Strings
再来一道模板题吧。
一道字符串 \(\texttt{hash}\) 。
我们可以利用 \(\texttt{hash}\) 的一个特点:当知道整个字符串的 \(\texttt{hash}\) 值时,就可以在 \(\mathcal{O} (1)\) 的时间负责度内算出其字串的 \(\texttt{hash}\) 值。
然后将原字符串进行拆分,使拆分出来的子字符串 \(\texttt{hash}\) 值相等即可。
双倍经验:link
#include <cstdio>
#include <iostream>
#include <cstring>
#define int unsigned long long
using namespace std;
const int base = 17;
int n;
char op[1000005];
int Hash[1000005], Pow[1000005];
void get_hash(char c[]) {
int len = strlen(c + 1);
for (int i = 1; i <= len; i++) {
Hash[i] = Hash[i - 1] * base + c[i];
}
}
int ans;
int get(int l, int r) {
return Hash[r] - Hash[l - 1] * Pow[r - l + 1];
}
signed main() {
Pow[1] = base;
for (int i = 2; i <= 1000000; i++) Pow[i] = Pow[i - 1] * base;
ios::sync_with_stdio(false);
while (cin >> op + 1) {
if (op[1] == '.') break;
get_hash(op);
int len = strlen(op + 1);
for (int i = 1; i <= len; i++) {
if (len % i != 0) continue;
int p1 = get(1, i);
bool f = true;
for (int j = i + 1; j <= len; j += i) {
int t = get(j, j + i - 1);
if (t != p1) {
f = false;
break;
}
}
if (f) {
printf("%d\n", len / i);
break;
}
}
}
return 0;
}
T3 [CEOI2017]Palindromic Partitions
用两个指针 \(L\) 和 \(R\) ,分别指向字符串首尾。从两端同时枚举找到相同的字符串, \(s[1 - L]\) 和 \(s[R - n]\), 然后下次再从 \(L\) 和 \(R\) 出发,继续找到相同的字符串,如果最后找到字符串的长度不足 \(n\) 就将答案加 \(1\)。
例如:\(\texttt{bonobo}\)
先找到 \(s[1 - 2]=s[5-6]=\texttt{"bo"}\),已匹配字符串长度 \(:4\)
然后我们就会发现剩下的字符无法匹配,而且我们发现已匹配字符串长度不足 \(n\)。所以剩下的 \(\texttt{"no"}\) 只能单独为一部分。
故最后答案为 \(3\)
对于匹配字符串,我们可以用 \(hash\) 来维护,两个字符串 \(hash\) 值相等,则两个字符串可以匹配。
code
#include <bits/stdc++.h>
#define int unsigned long long
using namespace std;
const int maxn = 1e6 + 5;
const int base = 31;
int Hash[maxn], Pow[maxn];
char s[maxn];
int gethash() {
int len = strlen(s + 1);
for (int i = 1; i <= len; i++) {
Hash[i] = Hash[i - 1] * base + s[i];
}
}
int get(int L, int R) {
return Hash[R] - Hash[L - 1] * Pow[R - L + 1];
}
int ans;
signed main() {
Pow[1] = base;
for (int i = 2; i <= maxn; i++) Pow[i] = Pow[i - 1] * base;
int t;
ios::sync_with_stdio(false);
cin >> t;
while (t--) {
cin >> s + 1;
gethash();
ans = 0;
int len = strlen(s + 1), st = 0;
int LL = 1, L = 1, RR = len, R = len;
while (L < R) {
if (get(LL, L) == get(R, RR)) {
ans += 2;
st += 2 * (L - LL + 1);
L++;
R--;
LL = L;
RR = R;
} else {
L++;
R--;
}
}
if (st < len) ans++;
cout << ans << endl;
}
return 0;
}
T4 花神游历各国
由于其特殊的操作 \(\sqrt{\delta_i}\), 我们可以发现从 \(\delta_i\) 的最大值开方到 \(1\) 最多仅需 \(6\) 次。于是我们就可以考虑线段树维护区间最大值,若该区间的最大值已经小于 \(1\),就可以直接跳过本次操作。否则直接枚举 \(l \sim r\) 单点修改即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int SIZE = 1e6;
int n, q;
int a[SIZE + 5];
struct tree {
int l, r;
int val;
} p[SIZE * 4], ma[SIZE * 4];
void build(int x, int l, int r) {
if (l == r) {
ma[x].l = l, ma[x].r = r;
ma[x].val = a[l];
p[x].l = l, p[x].r = r;
p[x].val = a[l];
return;
}
p[x].l = l, p[x].r = r;
ma[x].l = l, ma[x].r = r;
int mid = (l + r) >> 1;
build(x * 2, l, mid);
build(x * 2 + 1, mid + 1, r);
p[x].val = p[x * 2].val + p[x * 2 + 1].val;
ma[x].val = max(ma[x * 2].val, ma[x * 2 + 1].val);
}
void add(int t, int x) {
if (p[x].l == p[x].r) {
p[x].val = sqrt(p[x].val);
ma[x].val = sqrt(ma[x].val);
return;
}
int mid = (p[x].l + p[x].r) >> 1;
if (t <= mid) add(t, x * 2);
else add(t, x * 2 + 1);
p[x].val = p[x * 2].val + p[x * 2 + 1].val;
ma[x].val = max(ma[x * 2].val, ma[x * 2 + 1].val);
}
int ask1(int x, int l, int r) {
if (l <= p[x].l && p[x].r <= r) return p[x].val;
int mid = (p[x].l + p[x].r) >> 1;
int sum = 0;
if (l <= mid) sum += ask1(x * 2, l, r);
if (r > mid) sum += ask1(x * 2 + 1, l, r);
return sum;
}
int ask2(int x, int l, int r) {
if (l <= ma[x].l && ma[x].r <= r) return ma[x].val;
int mid = (ma[x].l + ma[x].r) >> 1;
int sum = INT_MIN;
if (l <= mid) sum = max(sum, ask2(x * 2, l, r));
if (r > mid) sum = max(sum, ask2(x * 2 + 1, l, r));
return sum;
}
int op, l, r, x;
signed main() {
scanf("%lld", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
build(1, 1, n);
scanf("%lld", &q);
while (q--) {
scanf("%lld", &op);
if (op == 1) {
scanf("%lld %lld", &l, &r);
if (l > r) swap(l, r);
printf("%lld\n", ask1(1, l, r));
} else {
scanf("%lld %lld", &l, &r);
if (l > r) swap(l, r);
if (ask2(1, l, r) <= 1) continue;
for (int i = l; i <= r; i++) {
// cout << i << endl;
if (a[i] == 1) continue;
a[i] = sqrt(a[i]);
add(i, 1);
}
}
}
return 0;
}
T5 立方体大作战
很妙的一道题目。
对于 \(a\) 值相等的两个数 \(l\) 和 \(r\) ,我们将左端点加 \(1\) ,右端点加 \(-1\),要使他们中间的数全部消除,需要的操作次数即为区间 \([l,r]\) 的和。
用树状数组维护就行了。
时间复杂度 \(O(n \log n)\)
#include <bits/stdc++.h>
//#define int long long
using namespace std;
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
char For_Print[25];
template <typename T>
void write (T x) {
if (x == 0) { putchar ('0'); return; }
if (x < 0) { putchar ('-'); x = -x; }
int poi = 0;
while (x) {
For_Print[++poi] = x % 10 + '0';
x /= 10;
}
while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
write (x); putchar (ch);
}
const int maxn = 50000;
int n;
int a[maxn * 2 + 5];
int L[maxn * 2 + 5], R[maxn * 2 + 5];
int p[maxn * 2 + 5];
int lowbit(int x) { return x & -x; }
void add(int x, int k) {
for (; x <= 2 * n; x += lowbit(x)) {
p[x] += k;
}
}
int ask(int x) {
int ans = 0;
for (; x; x -= lowbit(x)) {
ans += p[x];
}
return ans;
}
int ans;
signed main() {
read(n);
for (int i = 1; i <= 2 * n; i++) {
read(a[i]);
if (L[a[i]] == 0) L[a[i]] = i;
else R[a[i]] = i;
}
for (int i = 1; i <= 2 * n; i++) {
// cout << ask(i) << endl;
if (L[a[i]] == i) {
// cout << i << endl;
add(i, 1);
} else if (R[a[i]] == i) {
add(i, -1);
ans += ask(i) - ask(L[a[i]] - 1);
// cout << ask(i) << ' ' << ask(L[a[i]] - 1) << endl;
// cout << ask(i) - ask(L[a[i]] - 1) << endl;
add(i, 1);
add(L[a[i]], -1);
}
}
write(ans);
return 0;
}
T6 [ABC157E] Simple String Queries
link
分块好像不会被卡。
Solution.
我们可以用一个 \(b\) 数组来记录该块的每种字母的数量。
在每次查询的时候,我们可以新建一个桶,根据分块的基本思想,如果 \(l\) 和 \(r\) 在同一块,就直接将 \(l \sim r\) 之间的字母加入桶。否则就把 \(l\) 和 \(r\) 之间的块中的字母加入桶。最后枚举一下桶中有多少个不同的字母即可。
时间复杂度 \(O(26 × n \sqrt{n})\)
Code.
#include <bits/stdc++.h>
//#define int long long
using namespace std;
const int maxn = 1e6 + 5;
int n, q;
char s[maxn];
int op, l, r;
int pos[maxn], L[maxn], R[maxn];
int b[maxn][301];
int len, tot;
void init() {//分块
len = sqrt(n);
for (int i = 1; i + len <= n; i += len) {
L[++tot] = i;
R[tot] = i + len - 1;
}
if (R[tot] < n) {
tot++;
L[tot] = R[tot - 1] + 1;
R[tot] = n;
}
for (int i = 1; i <= tot; i++) {
for (int j = L[i]; j <= R[i]; j++) {
pos[j] = i;
b[i][s[j]]++;
}
}
}
void updata(int x, char c) {//单点修改
b[pos[x]][s[x]]--;
s[x] = c;
b[pos[x]][c]++;
}
bool p[301];
int ask(int l, int r) {
int t1 = pos[l], t2 = pos[r], ans = 0;
if (t1 == t2) {//如果在同一块
memset(p, 0, sizeof(p));
for (int i = l; i <= r; i++) {//直接将 l-r 加入
if (!p[s[i]]) {
p[s[i]] = true;
ans++;
}
}
} else {
memset(p, 0, sizeof(p));
for (int i = l; i <= R[t1]; i++) {
if (!p[s[i]]) {
p[s[i]] = true;
ans++;
}
}
for (int i = t1 + 1; i <= t2 - 1; i++) {//将块中的字母加入桶
for (int j = 'a'; j <= 'z'; j++) {
if (b[i][j] != 0) {
if (!p[j]) {
ans++;
p[j] = true;
}
}
}
}
for (int i = L[t2]; i <= r; i++) {
if (!p[s[i]]) {
p[s[i]] = true;
ans++;
}
}
}
return ans;
}
signed main() {
ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i++) cin >> s[i];
init();
cin >> q;
while (q--) {
cin >> op;
if (op == 1) {
char c;
cin >> l >> c;
updata(l, c);
} else if (op == 2) {
cin >> l >> r;
printf("%d\n", ask(l, r));
}
}
return 0;
}
本文来自博客园,作者:cqbzjyh,转载请注明原文链接:https://www.cnblogs.com/cqbzjyh/p/15549782.html

浙公网安备 33010602011771号