2025-11-11 hetao1733837的刷题记录
刷题背景:今天双11各大厂商搞促销,各位有没有性价比高的npy推荐一下/bx
P3810 【模板】三维偏序(陌上花开)
原题链接:P3810 【模板】三维偏序(陌上花开) - 洛谷
分析
CDQ分治的板子。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, k;
struct node{int a, b, c, cnt, res;bool operator!=(node other) const{if (a != other.a)return true;if (b != other.b)return true;if (c != other.c)return true;return false;}
}e[N], ue[N];
int m, t;
int ans[N];
int c[N << 1];
void add(int x, int v){for (int i = x; i <= k; i += i & (-i)){c[i] += v;}
}
int query(int x){int res = 0;for (int i = x; i; i -= i & (-i))res += c[i];return res;
}
bool cmp1(node x, node y){if (x.a != y.a)return x.a < y.a;if (x.b != y.b)return x.b < y.b;return x.c < y.c;
}
bool cmp2(node x, node y){if (x.b != y.b)return x.b < y.b;return x.c < y.c;
}
void CDQ(int l, int r){if (l == r)return ;int mid = (l + r) >> 1;CDQ(l, mid);CDQ(mid + 1, r);sort(ue + l, ue + mid + 1, cmp2);sort(ue + mid + 1, ue + r + 1, cmp2);int i = l, j = mid + 1;while (j <= r){while (i <= mid && ue[i].b <= ue[j].b){add(ue[i].c, ue[i].cnt);i++;}ue[j].res += query(ue[j].c);j++; }for (int k = l; k < i; k++)add(ue[k].c, -ue[k].cnt);return ;
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> k;for (int i = 1; i <= n; i++){cin >> e[i].a >> e[i].b >> e[i].c;}sort(e + 1, e + n + 1, cmp1);for (int i = 1; i <= n; i++){t++;if (e[i] != e[i + 1]){m++;ue[m].a = e[i].a;ue[m].b = e[i].b;ue[m].c = e[i].c;ue[m].cnt = t;t = 0;}}CDQ(1, m);for (int i = 1; i <= m; i++)ans[ue[i].res + ue[i].cnt - 1] += ue[i].cnt;for (int i = 0; i < n; i++)cout << ans[i] << '\n';return 0;
}
P3157 [CQOI2011] 动态逆序对
原题链接:P3157 [CQOI2011] 动态逆序对 - 洛谷
分析
一开始以为是神秘容斥,把修改受影响的删掉,得出最终答案(显然很难实现)。
但是,看了一眼题解发现我是**,记住:CDQ分治是一种离线算法!这样,就可以离线下来,做成或
的三维偏序了。
实现一下?
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, m;
struct node{int val, tim, res;
} a[N];
int c[N << 1];
int pos[N];
long long ans = 0;
void add(int x, int v) {for (int i = x; i <= n + m + 1; i += i & (-i))c[i] += v;
}
int query(int x){int res = 0;for (int i = x; i; i -= i & (-i))res += c[i];return res;
}
bool cmp1(node x, node y){if (x.val != y.val)return x.val < y.val;return x.tim < y.tim;
}
bool cmp2(node x, node y){return x.tim < y.tim;
}
void CDQ(int l, int r){if (l == r)return;int mid = (l + r) >> 1;CDQ(l, mid);CDQ(mid + 1, r);int i = l, j = mid + 1;while (i <= mid){while (j <= r && a[i].val > a[j].val){add(a[j].tim, 1);j++;}a[i].res += query(m + 1) - query(a[i].tim);i++;}for (int k = mid + 1; k < j; k++)add(a[k].tim, -1);i = mid, j = r;while (j > mid) {while (i >= l && a[i].val > a[j].val){add(a[i].tim, 1);i--;}a[j].res += query(m + 1) - query(a[j].tim);j--;}for (int k = mid; k > i; k--)add(a[k].tim, -1);sort(a + l, a + r + 1, cmp1);
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> m;for (int i = 1; i <= n; i++){cin >> a[i].val;pos[a[i].val] = i;a[i].tim = m + 1;a[i].res = 0;}for (int i = 1; i <= m; i++){int p;cin >> p;a[pos[p]].tim = i;}for (int i = n; i >= 1; i--){ans += query(a[i].val - 1);add(a[i].val, 1);}memset(c, 0, sizeof(c));CDQ(1, n);sort(a + 1, a + n + 1, cmp2);for (int i = 1; i <= m; i++) {cout << ans << '\n';ans -= a[i].res;}return 0;
}
P1908 逆序对
原题链接:P1908 逆序对 - 洛谷
分析
为啥突然写这题?因为蒟蒻不会树状数组求逆序对被HA rk6大佬mhh压力了/ll
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500005;
int n, c[N];
int cnt[N];
struct node{int val, id;
}a[N];
bool cmp(node x, node y){if (x.val == y.val)return x.id < y.id;return x.val < y.val;
}
void add(int x, int v){for (int i = x; i <= n; i += i & (-i))c[i] += v;
}
int query(int x){int ans = 0;for (int i = x; i; i -= i & (-i))ans += c[i];return ans;
}
signed main(){cin >> n;for (int i = 1; i <= n; i++){cin >> a[i].val;a[i].id = i;}sort(a + 1, a + n + 1, cmp);for (int i = 1; i <= n; i++){cnt[a[i].id] = i;}int ans = 0;for (int i = 1; i <= n; i++){add(cnt[i], 1);ans += i - query(cnt[i]);}cout << ans;
}
P2487 [SDOI2011] 拦截导弹
原题链接:P2487 [SDOI2011] 拦截导弹 - 洛谷
分析
这题显然是个DP,因为LG上有大量叫《导弹拦截》或者《拦截导弹》的题,基本都是DP。那么,我们要设置状态并给出转移,初始化以及答案。
我们设表示以第i枚导弹为最后一枚最多拦截的导弹数。
考虑转移,因为“以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度也不能大于前一发”,所以.(这显然已经是OI-Wiki给出的CDQ优化DP的标准式子了)。
初始化,
答案,
那么,概率呢?我们需要统计方案数。
我们设表示以第i枚结尾的最长序列的方案数。
考虑转移,
那么,概率只是简单地相除吗?🙅
一个导弹被拦截的概率等于包含它的最长拦截方案数/总最长拦截方案数
为了计算包含它的最长拦截方案数和总最长拦截方案数,我们要进行正向和反向两次DP,分别得到和
。
全局最长长度
全局总方案数
当且仅当时,导弹i在某个最长方案中。
则,。若不满足条件,
.
但是,复杂度来到了,无法通过,但是,从
就是明显的CDQ分治,那么,就可以直接开造了!
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, m;
struct node{int val, tim, res;
} a[N];
int c[N << 1];
int pos[N];
long long ans = 0;
void add(int x, int v) {for (int i = x; i <= n + m + 1; i += i & (-i))c[i] += v;
}
int query(int x){int res = 0;for (int i = x; i; i -= i & (-i))res += c[i];return res;
}
bool cmp1(node x, node y){if (x.val != y.val)return x.val < y.val;return x.tim < y.tim;
}
bool cmp2(node x, node y){return x.tim < y.tim;
}
void CDQ(int l, int r){if (l == r)return;int mid = (l + r) >> 1;CDQ(l, mid);CDQ(mid + 1, r);int i = l, j = mid + 1;while (i <= mid){while (j <= r && a[i].val > a[j].val){add(a[j].tim, 1);j++;}a[i].res += query(m + 1) - query(a[i].tim);i++;}for (int k = mid + 1; k < j; k++)add(a[k].tim, -1);i = mid, j = r;while (j > mid) {while (i >= l && a[i].val > a[j].val){add(a[i].tim, 1);i--;}a[j].res += query(m + 1) - query(a[j].tim);j--;}for (int k = mid; k > i; k--)add(a[k].tim, -1);sort(a + l, a + r + 1, cmp1);
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> m;for (int i = 1; i <= n; i++){cin >> a[i].val;pos[a[i].val] = i;a[i].tim = m + 1;a[i].res = 0;}for (int i = 1; i <= m; i++){int p;cin >> p;a[pos[p]].tim = i;}for (int i = n; i >= 1; i--){ans += query(a[i].val - 1);add(a[i].val, 1);}memset(c, 0, sizeof(c));CDQ(1, n);sort(a + 1, a + n + 1, cmp2);for (int i = 1; i <= m; i++) {cout << ans << '\n';ans -= a[i].res;}return 0;
}
P5979 [PA 2014] Druzyny
原题链接:P5979 [PA 2014] Druzyny - 洛谷
分析
“每一组都包含编号连续的一段小朋友”就指向了DP,否则无法转移,我想不出这题是否还有解。

好,那么我们先考虑暴力转移设表示区间
左端点最靠右的位置。
表示区间
右端点最靠左的位置。
表示区间
.
那么可得出状态即转移方程
转移显然是,也比较复合现在一些题目的套路,范围出在1e5左右,正解是
到
或
甚至
的优化。
显然,转移的形式是一个三维偏序的形式,而i,j这一维是天然有序的,因而不用排序,而更新来自一个区间,可以上数据结构——线段树,维护区间和(方案数)。呃,我应该理解了……吗?
正解
#include <bits/stdc++.h>
#define mod 1000000007
using namespace std;
const int N = 1000005;
int n;
pair<int, int> rng[N];
void update(pair<int, int> &x, const pair<int, int> &y){x.first = max(x.first, y.first);x.second = min(x.second, y.second);
}
namespace segtree{struct node{int mx, mc;node (int mx = 0xc0c0c0c0, int mc = 0) : mx(mx), mc(mc){}}tr[N << 2];int calc(int x){if (x < mod){return x;}elsereturn x - mod;}node operator+(const node &x, const node &y){if (x.mx == y.mx)return node(x.mx, calc(x.mc + y.mc));if (x.mx > y.mx)return x;return y;}void pushup(int p){tr[p] = tr[p << 1] + tr[p << 1 | 1];}void modify(int p, int l, int r, int s, const node &v){if (l == r){tr[p] = v;return ;}int mid = (l + r) >> 1;if (s <= mid)modify(p << 1, l, mid, s, v);elsemodify(p << 1 | 1, mid + 1, r, s, v);pushup(p);}node query(int p, int l, int r, int s, int t){if (t < l || r < s)return node();if (s <= l && r <= t){return tr[p];}int mid = (l + r) >> 1;return query(p << 1, l, mid, s, t) + query(p << 1 | 1, mid + 1, r, s, t);}int n;void init(int k){n = k;fill(tr + 1, tr + 1 + (n << 2), node());}void mdf(int s, const node &v){modify(1, 1, n, s, v);}node qry(int l, int r){return query(1, 1, n, l, r);}
}
using segtree::init;
using segtree::mdf;
using segtree::qry;
vector<pair<int, int>> id[N];
segtree::node dp[N];
void CDQ(int l, int r){if (r - l <= 50){for (int i = l; i <= r; i++){pair<int, int> tmp = {1, n};for (int j = i - 1; j >= l; j--){update(tmp, rng[j + 1]);if (i - j > tmp.second)break;if (i - j >= tmp.first)dp[i] = dp[i] + segtree::node(dp[j].mx + 1, dp[j].mc); }}return ;}int mid = (l + r) >> 1;CDQ(l, mid);pair<int, int> tmp = {1, n}; for (int i = mid + 1; i <= r; i++)id[i].clear();for (int i = mid; i >= l; i--){if (tmp.second < tmp.first || i + tmp.second <= mid)break;if (i + tmp.first <= r){id[max(mid + 1, i + tmp.first)].push_back({i, 1});id[min(r + 1, i + tmp.second + 1)].push_back({i, 0});}update(tmp, rng[i]);}init(mid - l + 1);tmp = {1, n};for (int i = mid + 1; i <= r; i++){for (auto j : id[i]){if (j.second)mdf(j.first - l + 1, dp[j.first]);elsemdf(j.first - l + 1, segtree::node());}update(tmp, rng[i]);if (tmp.second < tmp.first || i - tmp.second > mid)break;segtree::node res = qry(max(l, i - tmp.second) - l + 1, min(mid, i - tmp.first) - l + 1);++res.mx;dp[i] = dp[i] + res;}CDQ(mid + 1, r);
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n;pair<int, int> tmp{1, n};for (int i = 1; i <= n; i++){cin >> rng[i].first >> rng[i].second;update(tmp, rng[i]);}dp[0] = {0, 1};CDQ(0, n);if (dp[n].mx < 0)cout << "NIE";elsecout << dp[n].mx << " " << dp[n].mc;
}
P1340 兽径管理
原题链接:P1340 兽径管理 - 洛谷
分析
看题解理解了离线算法,倒着算可以省下每次排序的复杂度,还是比较优的,大概可以做到我没太认真算。但是,这题屎在可以跑w遍Kruskal,还是每次加边排序的那种*************懒得喷了。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 205, M = 6005;
int n, w, tot, fa[N];
long long ans[M];
bool vis[M << 1], del[M << 1];
struct node{int u, v, w, id;bool operator<(const node &tmp) const{return w < tmp.w;}
}e[M << 1];
void add(int u, int v, int w){e[++tot].v = v;e[tot].u = u;e[tot].w = w;
}
int getfa(int x){return x == fa[x] ? x : fa[x] = getfa(fa[x]);
}
long long kruskal(){memset(vis, 0, sizeof(vis));for (int i = 1; i <= n; i++)fa[i] = i;int cnt = 0;long long sum = 0;for (int i = 1; i <= tot; i++){if (del[e[i].id]) continue;int fu = getfa(e[i].u), fv = getfa(e[i].v);if (fu != fv){cnt++;sum += e[i].w;vis[e[i].id] = 1;fa[fu] = fv;}if (cnt == n - 1)break;}return cnt == n - 1 ? sum : -1;
}
int main(){cin >> n >> w;for (int i = 1; i <= w; i++){int u, v, w;cin >> u >> v >> w;add(u, v, w);e[i].id = i;}sort(e + 1, e + tot + 1);ans[w] = kruskal();for (int i = w - 1; i; i--){del[i + 1] = 1;if (vis[i + 1])ans[i] = kruskal();elseans[i] = ans[i+1];if (ans[i] == -1){for (int j = 1; j < i; j++)ans[j] = -1;break;}}for (int i = 1; i <= w; i++)cout << ans[i] << '\n';return 0;
}
P9869 [NOIP2023] 三值逻辑
原题链接:P9869 [NOIP2023] 三值逻辑 - 洛谷
分析
原始思路:对于那些绝对不可能相等的,比如只进行了一次取反……就直接判U……这么简单?不可能吧,应该分讨比较麻烦?好像也并非?看一眼题解。啥?并查集?我是**。
Trick引入:(抄的题解)取反运算过于繁琐,计算机不一定get到,那么把取反看成取相反数,所以,设置三个常量,分别用0,1e5+1,-1e5+1表示。
oh,最开始是有点对的,只是没有往实现上想,那就是并查集,如果x的祖先是-x或者U则必为无解。
那就是写代码了?
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int T = 100001, F = -100001, U = 0, N = 100005;
int c, t, n, m, ii, jj, fa[N];
bool vis[N * 3];
char s[N];
int getfa(int x){int ans;if (x == T || x == F)ans = x;else if (vis[n - x] || x == U)ans = U;else if (vis[x + n])ans = T;else if (x < 0){if (x == -fa[-x])ans = x;else{vis[x + n] = 1;ans = getfa(-fa[-x]);vis[x + n] = 0;} }else{if (x == fa[x])ans = x;else{vis[x + n] = 1;ans = fa[x] = getfa(fa[x]);vis[x + n] = 0;}}return ans;
}
signed main(){cin >> c >> t;while (t--){cin >> n >> m;for (int i = 1; i <= n; i++)fa[i] = i;for (int i = 1; i <= m; i++){cin >> s[i];if (s[i] == 'T'){cin >> ii;fa[ii] = T;}else if (s[i] == 'F'){cin >> ii;fa[ii] = F;}else if (s[i] == 'U'){cin >> ii;fa[ii] = U;}else{cin >> ii >> jj;if (s[i] == '+')fa[ii] = fa[jj];elsefa[ii] = -fa[jj];}}int ans = 0;for (int i = 1; i <= n; i++){if (getfa(i) == U)ans++;}cout << ans << '\n';;}
}
其实今天的题并非都弄懂了,好吧,还没有开VP……我怎么这么失败/ll
