2025-11-16~17 hetao1733837的刷题记录
11.16
[JSOI2008] 星球大战
原题链接:[JSOI2008] 星球大战
分析
很显然,对于一些多次修改的图论题 离线下来倒着做 \color{Black}{离线下来倒着做} 离线下来倒着做,有时候是很优的,特别是删边!
那么,顺理成章开始HE题解......
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 400005;
int n, m, ans[N], k, p[N];
bool vis[N];
int fa[N];
int head[N];
struct node{
int nxt, frm, to;
}e[N];
int tot;
void add(int u, int v){
e[++tot].frm = u;
e[tot].nxt = head[u];
head[u] = tot;
e[tot].to = v;
}
int find(int x){
return x == fa[x] ? fa[x] : fa[x] = find(fa[x]);
}
void merge(int x, int y){
int fx = find(x), fy = find(y);
if (fx != fy){
fa[fy] = fx;
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
memset(head, -1, sizeof(head));
for (int i = 0; i < n; i++){
fa[i] = i;
}
for (int i = 1; i <= m; i++){
int x, y;
cin >> x >> y;
add(x, y);
add(y, x);
}
cin >> k;
for (int i = 1; i <= k; i++){
cin >> p[i];
vis[p[i]] = 1;
}
int cnt = n - k;
for (int i = 1; i <= tot; i++){
if (!vis[e[i].frm] && !vis[e[i].to] && find(e[i].frm) != find(e[i].to)){
cnt--;
merge(e[i].frm, e[i].to);
}
}
ans[k + 1] = cnt;
for (int i = k; i >= 1; i--){
cnt++;
vis[p[i]] = 0;
for (int v = head[p[i]]; v != -1; v = e[v].nxt){
if (!vis[e[v].to] && find(p[i]) != find(e[v].to)){
cnt--;
merge(p[i], e[v].to);
}
}
ans[i] = cnt;
}
for (int i = 1; i <= k + 1; i++)
cout << ans[i] << '\n';
return 0;
}
「Wdsr-2.7」文文的摄影布置
原题链接:「Wdsr-2.7」文文的摄影布置
分析
19点49分
线段树?我印象中写过这题,但是没找到提交记录。那么问题就在于第三种操作,区间最小值也好差,但是, i i i和 k k k如何确定?容我稍考......显然 O ( n 2 ) O(n^2) O(n2)不可能放你,要不然不可能评蓝。
情绪有点低落啊......时间不晚......
20点09分
开始看题解。哦?问题转化为求:
max l ≤ a < b ≤ r A a − B b \max\limits_{l\le a<b\le r}{A_a-B_b} l≤a<b≤rmaxAa−Bb
max l ≤ b < c ≤ r A c − B b \max\limits_{l\le b<c\le r}{A_c-B_b} l≤b<c≤rmaxAc−Bb
那么,可以写代码了?
正解
操,写了一个小时!虽然水群了......
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 500005;
const int INF = 0x3f3f3f3f;
const int NEG_INF = 0xc0c0c0c0;
struct node{
int a, b, p, q, w;
node() : a(NEG_INF), b(INF), p(NEG_INF), q(NEG_INF), w(NEG_INF){}
node(int a_val, int b_val) : a(a_val), b(b_val), p(NEG_INF), q(NEG_INF), w(NEG_INF){}
node operator + (const node& tmp) const {
node k;
k.a = max(a, tmp.a);
k.b = min(b, tmp.b);
k.p = max({tmp.p, p, a - tmp.b});
k.q = max({tmp.q, q, tmp.a - b});
k.w = max({w, tmp.w, p + tmp.a, tmp.q + a});
return k;
}
};
int n, m;
int A[N], B[N];
struct segtree{
node tr[N << 2];
void pushup(int p, int l, int r, int s){
if (l == r){
tr[p] = node(A[l], B[l]);
return ;
}
int mid = (l + r) >> 1;
if (s <= mid)
pushup(p << 1, l, mid, s);
else
pushup(p << 1 | 1, mid + 1, r, s);
tr[p] = tr[p << 1] + tr[p << 1 | 1];
}
void build(int p, int l, int r){
if (l == r){
tr[p] = node(A[l], B[l]);
return ;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
tr[p] = tr[p << 1] + tr[p << 1 | 1];
}
node query(int p, int l, int r, int s, int t){
if (s <= l && r <= t){
return tr[p];
}
int mid = (l + r) >> 1;
if (t <= mid)
return query(p << 1, l, mid, s, t);
if (s > mid)
return query(p << 1 | 1, mid + 1, r, s, t);
return query(p << 1, l, mid, s, t) + query(p << 1 | 1, mid + 1, r, s, t);
}
}T;
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];
for (int i = 1; i <= n; i++)
cin >> B[i];
T.build(1, 1, n);
int op, l, r;
while (m--){
cin >> op >> l >> r;
if (op == 1){
A[l] = r;
T.pushup(1, 1, n, l);
}
else if (op == 2){
B[l] = r;
T.pushup(1, 1, n, l);
}
else if (op == 3){
node result = T.query(1, 1, n, l, r);
cout << result.w << '\n';
}
}
return 0;
}
楼房重建
原题链接:楼房重建
分析
能看到楼房,简单来说就是前面没有比它高的。难道是单调栈?复杂度让你飞起来,考虑线段树......居然还有分块!但我不会......
21点20分
线段树怎么维护呢?我稍考一下......
难道是区间最值等于本身?有点阴了,时间复杂度也不能接受吧......大概是 O ( m n l o g n ) O(mnlogn) O(mnlogn)?
21点24分
开看题解......喔,居然是顶点连线求凸包状物?有点nb了。哦,线段树维护区间最值及区间(从区间左端点开始,大于前一项必选,小于等于必不选的子序列长度)。合并时,左儿子一定全选,对于右儿子,最大值小于左儿子没有贡献,反之,找到大于左儿子最大值的位置继续递归处理。
我会写代码吗?
难点在于 p u s h u p 2 pushup2 pushup2。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100005;
int n, m;
int x, y;
double a[N];
struct segtree{
double mx[N << 2];
int len[N << 2];
void pushup1(int p){
mx[p] = max(mx[p << 1], mx[p << 1 | 1]);
}
int pushup2(int p, int l, int r, double maxn){
if (mx[p] <= maxn)
return 0;
if (a[l] > maxn)
return len[p];
if (l == r)
if (a[l] > maxn)
return 1;
else
return 0;
int ls = p << 1, rs = p << 1 | 1;
int mid = (l + r) >> 1;
if (mx[ls] <= maxn)
return pushup2(rs, mid + 1, r, maxn);
else
return pushup2(ls, l, mid, maxn) + len[p] - len[ls];
}
void modify(int p, int l, int r, int s, int v){
if (l == r && l == s){
mx[p] = (double)v / s;
len[p] = 1;
return ;
}
int mid = (l + r) >> 1;
if (s <= mid)
modify(p << 1, l, mid, s, v);
if (s > mid)
modify(p << 1 | 1, mid + 1, r, s, v);
pushup1(p);
len[p] = len[p << 1] + pushup2(p << 1 | 1, mid + 1, r, mx[p << 1]);
}
}T;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++){
cin >> x >> y;
a[x] = (double) y / x;
T.modify(1, 1, n, x, y);
cout << T.len[1] << '\n';
}
}
我是**,mx开成int了,居然还过了50分,数据有点水。
11.17
[JSOI2008] 最大数
原题链接:[JSOI2008] 最大数
分析
线段树动态开点因该可以吧......
11点42分
操了,电脑死机了。开he题解。
Oh,以前写过,不用动开,直接建空树,modify和query即可。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int M, a[N], D;
long long L, n;
char c;
struct Sgement_Tree{
#define mid (l + r >> 1)
int mx[N << 2];
void pushup(int p){
mx[p] = max(mx[p << 1], mx[p << 1 | 1]);
}
void build(int p, int l, int r){
if (l == r){
mx[p] = a[l];
return ;
}
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
int query(int p, int l, int r, int s, int t){
if (l >= s && r <= t)
return mx[p];
int ans = 0xc0c0c0c0;
if (s <= mid)
ans = query(p << 1, l, mid, s, t);
if (t > mid)
ans = max(ans, query(p << 1 | 1, mid + 1, r, s, t));
return ans;
}
void modify(int p, int l, int r, int s, int t){
if (l == r){
mx[p] += t;
return ;
}
if (s <= mid)
modify(p << 1, l, mid, s, t);
else
modify(p << 1 | 1, mid + 1, r, s, t);
pushup(p);
}
}T;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> M >> D;
long long lst = 0;
int tot = 0;
T.build(1, 1, M);
for (int i = 1; i <= M; i++){
cin >> c;
if (c == 'Q'){
cin >> L;
lst = T.query(1, 1, M, tot - L + 1, tot);
cout << lst << '\n';
}
else if (c == 'A'){
cin >> n;
++tot;
T.modify(1, 1, M, tot, (lst + n) % D);
}
}
}
I Hate It
原题链接:P1531 I Hate It
分析
什么?绿题一发过!虽然是板子吧......OK,线段树,还行。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, m, a[N];
struct segtree{
int mx[N << 2];
void pushup(int p){
mx[p] = max(mx[p << 1], mx[p << 1 | 1]);
}
void build(int p, int l, int r){
if (l == r){
mx[p] = a[l];
return ;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
int query(int p, int l, int r, int s, int t){
if (l >= s && r <= t){
return mx[p];
}
int ans = 0xc0c0c0c0;
int mid = (l + r) >> 1;
if (s <= mid)
ans = max(ans, query(p << 1, l, mid, s, t));
if (t > mid)
ans = max(ans, query(p << 1 | 1, mid + 1, r, s, t));
return ans;
}
void modify(int p, int l, int r, int s, int v){
if (l == r){
mx[p] = max(mx[p], v);
return ;
}
int mid = (l + r) >> 1;
if (s <= mid)
modify(p << 1, l, mid, s, v);
else
modify(p << 1 | 1, mid + 1, r, s, v);
pushup(p);
}
}T;
int main(){
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
T.build(1, 1, n);
while (m--){
int a, b;
char c;
cin >> c >> a >> b;
if (c == 'Q'){
cout << T.query(1, 1, n, a, b) << '\n';
}
if (c == 'U'){
T.modify(1, 1, n, a, b);
}
}
}
[JOISC 2014] JOIOJI
原题链接:[JOISC 2014] JOIOJI
分析
我是**,本题没有直接秒掉是因为对于 m a p map map不熟练,要在练习中多加打磨。
正解
cpp
#include<bits/stdc++.h>
using namespace std;
int n;
string s;
map<pair<int, int>, int> mp;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> s;
s = " " + s;
mp[{0, 0}] = 0;
int maxn = 0;
int J = 0, O = 0, I = 0;
for (int i = 1; i <= n; i++){
if (s[i] == 'J')
J++;
if (s[i] == 'O')
O++;
if (s[i] == 'I')
I++;
int tmp1 = J - O, tmp2 = O - I;
if (mp.find({tmp1, tmp2}) != mp.end()){
maxn = max(maxn, i - mp[{tmp1, tmp2}]);
}
else{
mp[{tmp1, tmp2}] = i;
}
}
cout << maxn;
}
[JOISC 2014] 电压 / Voltage
原题链接:[JOISC 2014] 电压 / Voltage
分析
是个图......啊?割边?还有个电阻的限制......限制一下奇偶性......
二分图???
20点55分
开始看题解,哦?图上奇偶性转化为 奇环、偶环 \color{Black}{奇环、偶环} 奇环、偶环。
那么,如果图中存在奇环,选的是所有奇环上边的交集且不得在偶环上。不存在奇环,随便选。
好吧,我并不理解,但代码可以he!
正解
忘了读入 n , m n,m n,m,我是**
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 100005, M = 200005;
struct node{
int nxt, v;
}e[M << 1];
int head[N];
int cnt = 1;
void add(int u, int v){
e[++cnt].nxt = head[u];
e[cnt].v = v;
head[u] = cnt;
}
int n, m;
bool vis[N], rt[N];
int de[N], sum[N][2], tot;
int f[N];
void dfs(int u){
vis[u] = 1;
for (int i = head[u]; i; i = e[i].nxt){
int v = e[i].v;
if (i == f[u] || (i ^ 1) == f[u])
continue;
if (vis[v]){
if (de[v] > de[u])
continue;
int d = (de[u] - de[v]) & 1;
sum[u][d]++;
sum[v][d]--;
if (!d)
tot++;
}
else{
f[v] = i;
de[v] = de[u] + 1;
dfs(v);
sum[u][0] += sum[v][0];
sum[u][1] += sum[v][1];
}
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++){
int u, v;
cin >> u >> v;
add(u, v);
add(v, u);
}
for (int i = 1; i <= n; i++){
if (!vis[i]){
rt[i] = 1;
dfs(i);
}
}
int ans = 0;
for (int i = 1; i <= n; i++){
if (sum[i][0] == tot && !sum[i][1] && !rt[i])
ans++;
}
if (tot == 1)
ans++;
cout << ans;
}
[JOISC 2014] 挂饰 / Straps
分析
很像背包,但是我不会???
操,我会了,设 d p i , j dp_{i,j} dpi,j表示前 i i i个物品, j j j个挂钩的最大收益。
那么,转移很容易,
d p i , j = max ( d p i , j , d p i − 1 , j − a i + b i ) dp_{i,j}=\max(dp_{i,j},dp_{i-1,j-a_i}+b_i) dpi,j=max(dpi,j,dpi−1,j−ai+bi)
还是很🐂🍺,转移方程一遍过,不过题解对 j − a i j-a_i j−ai和 0 0 0取了 max \max max,不太理解,觉得没啥必要。
正解
转移方程原理差不多,增加了一些。
cp
#include <bits/stdc++.h>
using namespace std;
const int N = 2005;
const int INF = 0x3f3f3f3f;
struct node {
int a, b;
} inp[N];
int n;
int f[N][N];
bool cmp(node x, node y){
return x.a > y.a;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> inp[i].a >> inp[i].b;
}
sort(inp + 1, inp + n + 1, cmp);
for (int i = 0; i <= n; i++){
for (int j = 0; j <= n + 1; j++){
f[i][j] = -INF;
}
}
f[0][1] = 0;
for (int i = 1; i <= n; i++){
for (int j = 0; j <= n; j++){
f[i][j] = max(f[i][j], f[i - 1][j]);
if (j >= 1 && f[i - 1][j] != -INF){
int v = j - 1 + inp[i].a;
v = min(v, n);
f[i][v] = max(f[i][v], f[i - 1][j] + inp[i].b);
}
}
}
int ans = 0;
for (int j = 0; j <= n; j++){
ans = max(ans, f[n][j]);
}
cout << ans << endl;
return 0;
}