【做题笔记】DS 专题训练
P1456 Monkey King
不会左偏树怎么办,直接启发式合并。
开个并查集,来判断敌友关系。随后遇上需要合并 \(x, y\),直接找到祖先然后启发式合并即可。
合并并查集同时合并一下堆,答案跟着一起维护即可,可以证明均摊时间复杂度为 \(O(q\log^2 n)\)。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N = 1e5 + 10;
int n, m, a[N], f[N];
priority_queue<int> q[N];
int find(int x) {
return (x == f[x] ? x : f[x] = find(f[x]));
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
while(cin >> n) {
For(i,1,n) priority_queue<int>().swap(q[i]);
For(i,1,n) {
cin >> a[i];
q[i].push(a[i]);
f[i] = i;
}
cin >> m;
For(i,1,m) {
int x, y;
cin >> x >> y;
if(find(x) == find(y)) cout << "-1\n";
else {
x = find(x), y = find(y);
int X = q[x].top(), Y = q[y].top();
q[x].pop(), q[y].pop();
q[x].push(X/2), q[y].push(Y/2);
if(q[x].size() > q[y].size()) swap(x, y);
while(!q[x].empty()) {
q[y].push(q[x].top());
q[x].pop();
}
cout << q[y].top() << '\n';
f[find(x)] = find(y);
}
}
}
return 0;
}
P3567 [POI2014] KUR-Couriers
考虑暴力将每一次询问 \([l,r]\) 中的数插入一个桶中,假设给定了桶的一个值域 \([L,R]\),设桶值域中的总和为 \(val\),我们发现若 \(2 \times val > r-l+1\) 则一定存在一个数的个数超过总区间长度的一半。
于是将所有数插入主席树中,对于询问 \([l,r]\),我们只要查找递归主席树 \([l,r]\) 内的左右儿子值域 \([L,mid],[mid+1,R]\) 来判断即可。
时间复杂度 \(O(q\log n)\)。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N = 5e5 + 10;
struct Node {
int ls, rs, val;
} t[N * 50];
int n, m, a[N], root[N], idx;
void pushup(int p) {
t[p].val = t[t[p].ls].val + t[t[p].rs].val;
}
void upd(int last, int &p, int l, int r, int x, int k) {
p = ++idx;
t[p] = t[last];
if(l == r) {
t[p].val += k;
return ;
}
int mid = l + r >> 1;
if(x <= mid) upd(t[last].ls, t[p].ls, l, mid, x, k);
else upd(t[last].rs, t[p].rs, mid + 1, r, x, k);
pushup(p);
}
int query(int p1, int p2, int x, int l, int r) {
if(l == r) return l;
int mid = l + r >> 1;
if(2 * (t[t[p2].ls].val - t[t[p1].ls].val) > x) return query(t[p1].ls, t[p2].ls, x, l, mid);
if(2 * (t[t[p2].rs].val - t[t[p1].rs].val) > x) return query(t[p1].rs, t[p2].rs, x, mid + 1, r);
return 0;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m;
For(i,1,n) {
cin >> a[i];
upd(root[i-1], root[i], 1, n, a[i], 1);
}
For(i,1,m) {
int l, r;
cin >> l >> r;
cout << query(root[l-1], root[r], r-l+1, 1, n) << '\n';
}
return 0;
}
P5482 [JLOI2011] 不等式组
好题。
首先看到维护不等式很容易想到树状数组。然后考虑一次函数的代价,也就是 \(a\) 的正负会带来什么影响。当 \(a>0\) 时,\(x>\dfrac{c-b}{a}\);当 \(a<0\) 时,\(x<\dfrac{c-b}{a}\);因为 \(x\) 只能为整数,所以上述条件与当 \(a>0\) 时, \(x>\left\lfloor\dfrac{c-b}{a}\right\rfloor\);当 \(a<0\) 时,\(x<\left\lceil\dfrac{c-b}{a}\right\rceil\) 等价。
于是就用值域分别为 \([-10^6,0)\),\([0,10^6]\) 的两个树状数组来维护即可。注意要判断一下恒成立和恒不成立的不等式,并且要注意删除的时候记得消除贡献并标记删除。
时间复杂度 \(O(n\log n)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define MAX_F 2000002
#define L 1000001
using namespace std;
const int N = 2e6 + 10;
struct Node {
int a, b, c, id;
} p[N];
int n, cnt, lon, t1[N], t2[N], k[N];
bool del[N];
int lb(int x) {
return x & -x;
}
int qry(int x, int op) {
x += L;
int ans = 0;
for (int i = x; i; i -= lb(i)) {
ans += (op == 1 ? t1[i] : t2[i]);
}
return ans;
}
void upd(int x, int k, int op) {
x += L;
for (int i = x; i <= MAX_F; i += lb(i)) {
(op == 1 ? t1[i] : t2[i]) += k;
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
while(n--) {
string op;
int a, b, c, i, x;
cin >> op;
if(op == "Add") {
cin >> a >> b >> c;
p[++cnt] = (Node){a, b, c, (a>0)+1};
if(!a) {
if(b > c) p[cnt].id = 3, lon++;
else p[cnt].id = 0;
} else if(a < 0) {
k[cnt] = ceil((c * 1.0 - b) / a);
p[cnt].id = 1;
if(k[cnt] < -1e6) p[cnt].id = 0;
else if(k[cnt] > 1e6) {
lon++;
p[cnt].id = 3;
} else {
upd(k[cnt], 1, 1);
}
} else {
k[cnt] = floor((c * 1.0 - b) / a);
p[cnt].id = 2;
if(k[cnt] > 1e6) p[cnt].id = 0;
else if(k[cnt] < -1e6) {
lon++;
p[cnt].id = 3;
} else {
upd(k[cnt], 1, 2);
}
}
} else if(op == "Del") {
cin >> i;
if(del[i]) continue;
del[i] = 1;
if(p[i].id == 3) lon--;
else if(p[i].id == 1) upd(k[i], -1, 1);
else if(p[i].id == 2) upd(k[i], -1, 2);
} else {
cin >> x;
cout << qry(x-1, 2) + (qry(1e6, 1) - qry(x, 1)) + lon << '\n';
}
}
return 0;
}