63-61: 模拟赛
Sol1:
n<=10,直接搜索选了那些点即可。
复杂度$O(2^n)$,期望得分20分。
Sol2:
数据是一条链。
那么条件转化为:选了一个点后,它后面中所有的点的权值比他大。
那么求一遍最长上升子序列即可。
复杂度$O(nlogn)$。
Sol3:
根据sol2的做法,如果将树转化为dfs序,那么就和sol3的做法一样了。
注意dfs的过程中,先遍历右节点,然后左节点。
复杂度$O(nlogn)$,期望得分100分。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cctype> using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar())if (ch == '-')f = -1; for (; isdigit(ch); ch = getchar())x = x * 10 + ch - '0'; return x * f; } const int N = 100005; int w[N], ls[N], rs[N], q[N], f[N], tot; void dfs(int u) { if (!u) return ; q[++tot] = u; dfs(rs[u]); dfs(ls[u]); } int main() { freopen("point.in", "r", stdin); freopen("point.out", "w", stdout); int n = read(); for (int i = 1; i <= n; ++i) w[i] = read(); for (int i = 1; i <= n; ++i) ls[i] = read(), rs[i] = read(); dfs(1); for (int i = 1; i <= n; ++i) q[i] = w[q[i]]; int cnt = 1; f[1] = q[1]; for (int i = 2; i <= n; ++i) { if (q[i] > f[cnt]) f[++cnt] = q[i]; else { int pos = lower_bound(f + 1, f + cnt + 1, q[i]) - f; f[pos] = q[i]; } } cout << cnt; return 0; }
动态开点线段树直接模拟
会T,毕竟常数巨大
// 这个代码写的有点zz啊 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> using namespace std; #define Rep(i, a, b) for(register int i = a; i <= b; i ++) #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<22, stdin), p1 == p2) ? EOF : *p1++) char buf[(1 << 22)], *p1 = buf, *p2 = buf; #define gc getchar() inline int read() { int x = 0, f = 1; char c = gc; while(c < '0' || c > '9') {if(c == '-') f = -1; c = gc;} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc; return x * f; } #define LL long long const int N = 1e5 + 5; int data[N * 32], Root[N * 32], Lson[N * 32], Rson[N * 32]; int n; int jd; struct Node { int x, y; } ask[N]; int Max; int Poi_A(int l, int r, int &rt, int x) { if(!rt) rt = ++ jd; if(l == r) { if(!data[rt]) { data[rt] = l; return l; } else { return data[rt]; } } int mid = (l + r) >> 1; if(x <= mid) return Poi_A(l, mid, Lson[rt], x); else return Poi_A(mid + 1, r, Rson[rt], x); } void Poi_G(int l, int r, int rt, int x, int num) { if(!rt) return ; if(l == r) { data[rt] = num; return ; } int mid = (l + r) >> 1; if(x <= mid) Poi_G(l, mid, Lson[rt], x, num); else Poi_G(mid + 1, r, Rson[rt], x, num); } struct Node1 { int num, w; } G[N * 3]; int tot; void Dfs(int l, int r, int rt) { if(!rt) return ; if(l == r) { G[++ tot].num = data[rt]; G[tot].w = l; return ; } int mid = (l + r) >> 1; Dfs(l, mid, Lson[rt]); Dfs(mid + 1, r, Rson[rt]); } int Imp; void Sec_A(int l, int r, int rt, int x, int y) { if(!rt) return ; if(x <= l && r <= y) { Imp += data[rt]; return ; } if(l == r) return ; int mid = (l + r) >> 1; if(x <= mid) Sec_A(l, mid, Lson[rt], x, y); if(y > mid) Sec_A(mid + 1, r, Rson[rt], x, y); } void Poi_Mod(int l, int r, int &rt, int x) { if(!rt) rt = ++ jd; data[rt] ++; if(l == r) return ; int mid = (l + r) >> 1; if(x <= mid) Poi_Mod(l, mid, Lson[rt], x); else Poi_Mod(mid + 1, r, Rson[rt], x); } int tdata[N * 32], tRoot[N * 32], tLson[N * 32], tRson[N * 32]; int tjd; void tSec_A(int l, int r, int rt, int x, int y) { if(!rt) return ; if(x <= l && r <= y) { Imp += tdata[rt]; return ; } if(l == r) return ; int mid = (l + r) >> 1; if(x <= mid) tSec_A(l, mid, tLson[rt], x, y); if(y > mid) tSec_A(mid + 1, r, tRson[rt], x, y); } void tPoi_Mod(int l, int r, int &rt, int x) { if(!rt) rt = ++ tjd; tdata[rt] ++; if(l == r) return ; int mid = (l + r) >> 1; if(x <= mid) tPoi_Mod(l, mid, tLson[rt], x); else tPoi_Mod(mid + 1, r, tRson[rt], x); } int main() { freopen("a.in", "r", stdin); freopen("a.out", "w", stdout); n = read(); Rep(i, 1, n) { int x = read(), y = read(); if(x > y) swap(x, y); ask[i].x = x, ask[i].y = y; Max = max(Max, ask[i].y); } Rep(i, 1, n) { int x = Poi_A(1, Max, Root[1], ask[i].x); int y = Poi_A(1, Max, Root[1], ask[i].y); Poi_G(1, Max, Root[1], ask[i].x, y); Poi_G(1, Max, Root[1], ask[i].y, x); } Dfs(1, Max, Root[1]); memset(Root, 0, sizeof Root); memset(Lson, 0, sizeof Lson); memset(Rson, 0, sizeof Rson); memset(data, 0, sizeof data); jd = 0; LL Answer = 0; Rep(i, 1, tot) { tPoi_Mod(1, Max, tRoot[1], G[i].w); } Rep(i, 1, tot) { int x = G[i].num, w = G[i].w; Imp = 0; Sec_A(1, Max, Root[1], x + 1, Max); Answer += Imp; int l, r; if(abs(x - w) < 2) { Poi_Mod(1, Max, Root[1], x); continue; } if(x > w) { l = w + 1, r = x - 1; Imp = 0; tSec_A(1, Max, tRoot[1], l, r); Answer += (r - l + 1 - Imp); } else { l = x + 1, r = w - 1; Imp = 0; tSec_A(1, Max, tRoot[1], l, r); Answer += (r - l + 1 - Imp); } Poi_Mod(1, Max, Root[1], x); } cout << Answer; return 0; }
#include<cstdio> #include<algorithm> #include<stack> #include<queue> #include<cmath> #include <iostream> #define LL long long #define lb(x) (x & (-x)) #define Pair pair<int, int> #define fi first #define se second #define MP(x, y) make_pair(x, y) using namespace std; const int MAXN = 1e6 + 10; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int N; int T[MAXN], date[MAXN], pos[MAXN]; LL sum[MAXN]; // val[i] i位置的元素 // date 离散化数组 Pair P[MAXN]; void add(int x) { for(int i = x; i <= N * 2; i += lb(i)) T[i]++; } LL Query(int x) { LL ans = 0; for(int i = x; i; i -= lb(i)) ans += T[i]; return ans; } int main() { N = read(); for(int i = 1; i <= N; i++) { P[i].fi = read(), P[i].se = read(); date[i] = P[i].fi; date[i + N] = P[i].se; pos[i] = i; pos[i + N] = i + N; } sort(date + 1, date + N * 2 + 1); int num = unique(date + 1, date + N * 2 + 1) - date - 1; for(int i = 1; i <= num; i++) sum[i] = sum[i - 1] + date[i] - date[i - 1] - 1; for(int i = 1; i <= N; i++) { P[i].fi = lower_bound(date + 1, date + num + 1, P[i].fi) - date; P[i].se = lower_bound(date + 1, date + num + 1, P[i].se) - date; swap(pos[P[i].fi], pos[P[i].se]); } LL ans = 0; for(int i = num; i >= 1; i--) { ans += Query(pos[i]); ans += abs(sum[pos[i]] - sum[i]); add(pos[i]); } std:: cout << ans; return 0; }
这道题目考察的是一点思路加上树上的一些性质。
可以说是vector加上启发式合并的一道题目吧。
思路抄袭借鉴于[HNOI2009]梦幻布丁(好吧我只是把它搬到了树上)
题目意思是在树上统计同色联通快个数,更改颜色。
30pts:
我们发现给定我们树上的所有节点的颜色,我们可以O(n)求得军团个数,方法是进行dfs,如果一个结点和他父亲节点颜色不同,答案就++
然后暴力更改暴力统计答案
30pts2:
我们可以优化一下上面那个暴力,用vector纪录下每种颜色存在的位置,然后枚举要更改的颜色位置,每个位置原来对于答案的贡献是和他相邻不同色的节点个数,我们减去原来的答案加上更改后的答案就得到了当前的答案。然后再暴力合并vector就行了。
20pts:是一条链,就是梦幻布丁那道题,不再细说了
100pts: 我们考虑优化合并操作,我们发现把x变成y和把y变成x得到的答案是相同的,所以我们在合并的时候以及更改颜色的时候都是把颜色少的改成颜色多的,并且维护每种颜色实际的vector是哪个,然后就相当于启发式合并。
每个点的复杂度是他的度数乘以它被统计答案的次数,最差情况下每个点也只会被统计logN次答案,那么复杂度就是logN * \sum 度数 等于2NlogN
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<iostream> #include<ctime> #include<cmath> #include<set> #include<map> #define ll long long #define M 200010 using namespace std; int read() { int nm = 0, f = 1; char c = getchar(); for(; !isdigit(c); c = getchar()) if(c == '-') f = -1; for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0'; return nm * f; } int note[M], sz[M], cor[M], id[M]; vector<int>to[M], to1[M]; int n, q, ans; void dfs(int now, int fa) { if(cor[now] != 0 && cor[now] != cor[fa]) ans++; for(int i = 0; i < to[now].size(); i++) { int vj = to[now][i]; if(vj == fa) continue; dfs(vj, now); } } void del(int x) { for(int i = 0; i < to[x].size(); i++) { int vj = to[x][i]; if(cor[vj] != cor[x]) ans--; } } void insert(int x) { for(int i = 0; i < to[x].size(); i++) { int vj = to[x][i]; if(cor[vj] != cor[x]) ans++; } } int tot = 0, tot2 = 0; int main() { n = read(), q = read(); for(int i = 1; i <= n; i++) cor[i] = read(), sz[cor[i]]++, to1[cor[i]].push_back(i), id[i] = i, note[i] = i; for(int i = 1; i < n; i++) { int vi = read(), vj = read(); to[vi].push_back(vj), to[vj].push_back(vi); } to[1].push_back(0), cor[0] = 0x3e3e3e3e; dfs(1, 0); while(q--) { int x = read(), y = read(); int xn = id[x], yn = id[y]; if(sz[xn] < sz[yn]) { tot += sz[xn], tot2 += to1[xn].size(); for(int i = 0; i < to1[xn].size(); i++) { int op = to1[xn][i]; del(op); to1[yn].push_back(op); } for(int i = 0; i < to1[xn].size(); i++) { int op = to1[xn][i]; cor[op] = yn; } for(int i = 0; i < to1[xn].size(); i++) { int op = to1[xn][i]; insert(op); } to1[xn].clear(); sz[yn] += sz[xn]; sz[xn] = 0; id[x] = 0; } else { tot+=sz[yn], tot2 += to1[yn].size(); for(int i = 0; i < to1[yn].size(); i++) { int op = to1[yn][i]; del(op); to1[xn].push_back(op); } for(int i = 0; i < to1[yn].size(); i++) { int op = to1[yn][i]; cor[op] = xn; } for(int i = 0; i < to1[yn].size(); i++) { int op = to1[yn][i]; insert(op); } to1[yn].clear(); sz[xn] += sz[yn]; sz[yn] = 0; id[y] = xn; id[x] = 0; } cout << ans << "\n"; } return 0; }