2023 做题记录
就是 2023 做过的个人觉得还不错的题。
1 CF1601D Difficult Mountain
Problem
Sol
忘了当时是从哪里看到的了。但是那时候猜了一手按
考虑证明这个结论。
先把人分为两类:
所以要按照
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5e5 + 5;
int n, d;
struct node {
int s, a, mx;
friend bool operator < (node x, node y) {
return x.mx != y.mx ? x.mx < y.mx : x.s < y.s;
}
} a[N];
int main() {
cin >> n >> d;
for (int i = 1; i <= n; i++) {
cin >> a[i].s >> a[i].a;
a[i].mx = max(a[i].s, a[i].a);
}
sort(a + 1, a + n + 1);
int ans = 0;
for (int i = 1; i <= n; i++)
if (d <= a[i].s) {
ans++;
d = max(d, a[i].a);
}
cout << ans << endl;
return 0;
}
2 CF690A3 Collective Mindsets (hard)
Problem
一共有
Sol
一些构造题如果
Code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int read() {
int ret = 0, sgn = 0;
char ch = getchar();
while (!isdigit(ch)) sgn |= ch == '-', ch = getchar();
while (isdigit(ch)) ret = ret * 10 + ch - '0', ch = getchar();
return sgn ? -ret : ret;
}
int main() {
int T = read();
while (T--) {
int n = read(), r = read(), sum = 0;
for (int i = 1; i < n; i++) {
int x = read();
sum += x;
}
cout << (r - sum % n + n) % n + 1 << endl;
// 这里要 +1, 因为之前是按余数算的,但要求的是 1~n。
}
return 0;
}
3 CF727F Polycarp's problems
Problem
给定一个长为
Ex:存在
Sol
虽然这道题的 dp 是比较显然的,贪心做法却比较妙。
先说 dp。如果只有一次询问的话,令
然后在
然后说一下贪心的做法。就是有一个贪心就是用一个正数去消多个负数,然后最后剩下的消不掉的就由
考虑怎么解决掉这个问题,发现是因为前面的决策后面就考虑不到了,所以就不能局部贪心了,所以倒过来就行了。然后用堆维护并在查询时二分即可做到
Code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 755;
int n, m;
ll a[N], tp, sum[N];
priority_queue <int> q;
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = n; i; i--) {
if (a[i] < 0) q.push(a[i]);
else {
while (!q.empty() && a[i] >= 0) {
a[i] += q.top();
q.pop();
}
if (a[i] < 0) q.push(a[i]);
}
}
while (!q.empty()) {
sum[++tp] = -q.top();
q.pop();
}
for (int i = 1; i <= tp; i++) sum[i] += sum[i - 1];
while (m--) {
ll x;
scanf("%lld", &x);
int res;
if (x >= sum[tp]) res = 0;
else res = tp - (upper_bound(sum + 1, sum + tp + 1, x) - sum) + 1;
printf("%d\n", res);
}
return 0;
}
4 AGC058C Planar Tree
Problem
给定一个
Sol
当时看了题解,现在自己做出来了。
首先如果有连续的一段的
然后这样就可以通过一些操作抹掉所有的
考虑什么情况下才能消掉所有的
实现上可以先从左到右扫一遍,然后再从右到左做一遍就行。注意枚举到
Code
#include<bits/stdc++.h>
using namespace std;
#define Fin(file) freopen(file,"r",stdin)
#define Fout(file) freopen(file,"w",stdout)
#define L(i,j,k) for(int i=(j);i<=(k);++i)
#define R(i,j,k) for(int i=(j);i>=(k);--i)
int n;
int a[300010],vis[300010],c[5];
void solve(){
cin>>n;
L(i,1,n)cin>>a[i],vis[i]=0;
L(_,0,1){
int pre=-1;
L(i,1,2*n)if(!vis[(i-1)%n+1]){
int v=a[(i-1)%n+1];
if((pre==v)||(pre==2&&v==1)||(pre==3&&v==4))vis[(i-1)%n+1]=1;
else pre=a[(i-1)%n+1];
}
reverse(a+1,a+n+1);reverse(vis+1,vis+n+1);
}
L(i,1,4)c[i]=0;
L(i,1,n)if(!vis[i])++c[a[i]];
cout<<((c[4]>=c[2]||c[1]>=c[3])?"No":"Yes")<<"\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)solve();
return 0;
}
5 P8565 Sultan Rage
Problem
有一个数列
Sol
发现数列
发现值域上点的分布越来越稀疏且点极少,可以考虑搜索,函数 dfs(val, cur)
表示凑出
但光是搜索肯定不能过这道题,考虑优化。
先记忆化掉重复的操作,可以用一个 map 来存储,然后可以进行可行性剪枝,如果以后再怎么凑都不行,直接剪枝,可以用前缀和优化一下。
但是这样只有 30 分。
可以改变一下搜索的顺序,因为先考虑小元素的话,会有较多的无用的搜索,且小元素较灵活,更容易凑到
注意:判断能否返回时是 mp[cur].conut(val)
,而不是 mp[cur][val]
,否则会超时。因为如果这样判断的话,会直接额外开一个节点,对每一层都是如此,但第一次搜索时会额外开
时空可过。
Code
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
ll read() {
ll ret = 0, sgn = 0;
char ch = getchar();
while (!isdigit(ch)) sgn |= ch == '-', ch = getchar();
while (isdigit(ch)) ret = ret * 10 + ch - '0', ch = getchar();
return sgn ? -ret : ret;
}
const int N = 180, mod = 998244353;
const ll INF = 1e18;
int n, q;
ll a[N], sum[N];
map<ll, int> mp[N];
int dfs(ll x, int cur) {
if (x < 0 || x > sum[cur]) return 0;
if (!cur) return (x == 0);
if (mp[cur].count(x)) return mp[cur][x];
return mp[cur][x] = (dfs(x - a[cur], cur - 1) + dfs(x, cur - 1)) % mod;
}
int main() {
int T = read();
while (T--) {
n = read(), q = read();
for (int i = 1; i <= n; i++) a[i] = read();
int m = n;
while (a[m] <= INF) {
a[++m] = 0;
for (int i = 1; i <= n; i++) a[m] += a[m - i];
}
n = m - 1;
for (int i = 1; i <= n; i++) {
sum[i] = sum[i - 1] + a[i];
mp[i].clear();
}
while (q--) {
ll x = read();
printf("%d\n", dfs(x, n));
}
}
return 0;
}
6 ABC298H Sum of Min of Length
Problem
给定一棵
Sol
因为是求
令
则
容易想到求一个点到其他点的距离和。
令
在 dfs 时处理一下即可,显然可以做到
最后把距离分奇偶讨论一下即可。
时间复杂度:
Code
#include<bits/stdc++.h>
#define pb emplace_back
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
const int inf = 2147483647, mod = 1011451423;
const ll INF = 1e18;
const int N = 2e5 + 10;
int n, m;
int siz[N], d[N], fa[N][20];
ll dis[N], v[N];
vector<int> e[N];
void dfs1(int u, int f) {
d[u] = d[f] + 1, siz[u] = 1, fa[u][0] = f;
for (int i = 1; i <= 18; i++)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (int v : e[u])
if (v != f) {
dfs1(v, u);
siz[u] += siz[v];
dis[u] += dis[v] + siz[v];
}
}
void dfs2(int u, int f) {
if (u == 1)
v[u] = dis[u];
else
v[u] = v[f] + (siz[1] - siz[u]) - siz[u];
for (int v : e[u])
if (v != f)
dfs2(v, u);
}
int lca(int u, int v) {
if (d[u] < d[v])
swap(u, v);
for (int i = 18; i >= 0; i--)
if (d[fa[u][i]] >= d[v])
u = fa[u][i];
if (u == v)
return u;
for (int i = 18; i >= 0; i--)
if (fa[u][i] != fa[v][i])
u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
int tonode(int u, int st) {
for (int i = 18; i >= 0; i--)
if (st >> i & 1)
u = fa[u][i];
return u;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v;
e[u].pb(v), e[v].pb(u);
}
dfs1(1, 0), dfs2(1, 0);
cin >> m;
while (m--) {
int l, r;
cin >> l >> r;
if (d[l] < d[r])
swap(l, r);
int p = lca(l, r), dist = d[l] + d[r] - 2 * d[p];
if (dist % 2 == 0) {
int now = tonode(l, dist / 2);
cout << v[l] + v[r] - v[now] - 1ll * n * dist / 2 << "\n";
} else {
int now = tonode(l, dist / 2);
cout << v[l] + v[r] - v[now] - 1ll * n * (dist / 2) - siz[now] << "\n";
}
}
return 0;
}
7 P7834 [ONTAK2010] Peaks 加强版
Problem
给定一张
有
Sol
发现有只经过权值
下面的代码是可持久化线段树合并写法 by Alex_Wei。
Code
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define fi first
#define se second
#define TIME 1e3 * clock() / CLOCKS_PER_SEC
using ll = long long;
using uint = unsigned int;
using ld = long double;
// using lll = __int128;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using ull = unsigned long long;
inline ll read() {
ll x = 0, sgn = 0;
char s = getchar();
while(!isdigit(s)) sgn |= s == '-', s = getchar();
while(isdigit(s)) x = x * 10 + s - '0', s = getchar();
return sgn ? -x : x;
}
inline void print(ll x) {
if(x < 0) return putchar('-'), print(-x);
if(x >= 10) print(x / 10);
putchar(x % 10 + '0');
}
bool Mbe;
constexpr int N = 1e5 + 5;
constexpr int M = 5e5 + 5;
constexpr int V = N << 1;
constexpr int K = N * 40;
// linklist
int cnt, hd[V], nxt[V], to[V];
void add(int u, int v) {nxt[++cnt] = hd[u], hd[u] = cnt, to[cnt] = v;}
// dsu
int fa[V];
int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]);}
struct SegTree {
int node, ls[K], rs[K], val[K];
void modify(int l, int r, int p, int &x) {
if(!x) x = ++node;
val[x]++;
if(l == r) return;
int m = l + r >> 1;
if(p <= m) modify(l, m, p, ls[x]);
else modify(m + 1, r, p, rs[x]);
}
int merge(int x, int y) {
if(!x || !y) return x | y;
int z = ++node;
ls[z] = merge(ls[x], ls[y]);
rs[z] = merge(rs[x], rs[y]);
return val[z] = val[x] + val[y], z;
}
int binary(int l, int r, int x, int k) {
if(l == r) return l;
int m = l + r >> 1;
if(val[rs[x]] >= k) return binary(m + 1, r, rs[x], k);
return binary(l, m, ls[x], k - val[rs[x]]);
}
} tr;
// basic
int n, m, q, a[N], d[N];
// kruskal
int node, lg, val[V], R[V], anc[18][V];
struct edge {
int u, v, w;
bool operator < (const edge &x) const {return w < x.w;}
} e[M];
void dfs(int id) {
if(id <= n) tr.modify(1, n, a[id], R[id]);
for(int i = hd[id]; i; i = nxt[i]) {
int it = to[i];
anc[0][it] = id, dfs(it);
R[id] = tr.merge(R[id], R[it]);
}
}
bool Med;
int main() {
fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0);
#ifdef ALEX_WEI
FILE* IN = freopen("1.in", "r", stdin);
FILE* OUT = freopen("1.out", "w", stdout);
#endif
cin >> n >> m >> q;
for(int i = 1; i <= n; i++) d[i] = a[i] = read();
sort(d + 1, d + n + 1);
for(int i = 1; i <= n; i++) a[i] = lower_bound(d + 1, d + n + 1, a[i]) - d;
for(int i = 1; i <= m; i++) e[i].u = read(), e[i].v = read(), e[i].w = read();
sort(e + 1, e + m + 1), node = n;
for(int i = 1; i <= n + n; i++) fa[i] = i;
for(int i = 1; i <= m; i++) {
int u = find(e[i].u), v = find(e[i].v);
if(u == v) continue;
val[++node] = e[i].w, fa[u] = fa[v] = node;
add(node, u), add(node, v);
}
lg = 31 - __builtin_clz(node), dfs(node);
for(int i = 1; i <= lg; i++)
for(int j = 1; j <= node; j++)
anc[i][j] = anc[i - 1][anc[i - 1][j]];
for(int _ = 1, lst = 0; _ <= q; _++) {
int u = (read() ^ lst) % n + 1, x = read() ^ lst;
int k = (read() ^ lst) % n + 1;
for(int i = lg; ~i; i--) {
int v = anc[i][u];
if(v && val[v] <= x) u = v;
}
if(tr.val[R[u]] < k) print(-1), lst = 0;
else print(lst = d[tr.binary(1, n, R[u], k)]);
putchar('\n');
}
cerr << TIME << " ms\n";
return 0;
}
8 P5313 [Ynoi2011] WBLT
Problem
给定一个长为
保证所有数均在
Sol
看到支持离线,可以想到莫队。
先想一下暴力是怎么做的:每次枚举
考虑怎么转化到莫队上去,莫队左右端点
考虑先将桶按
当
但是发现当
现在考虑
由于
这时就只需在
这一部分的时间复杂度为
时间复杂度:
代码好像有 UB /gg。
Code
#include<bits/stdc++.h>
#define pb push_back
#define ios ios::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 2147483647, mod = 1e9 + 7;
const ll INF = 1e18;
const int N = 1e5 + 10, V = 1e5 + 1, MOD = 63, W = 64, bit = 6;
int n, m, len, cq, nowb;
int a[N], cnt[N], ans[N];
ull filter[W];
struct que {
int l, r, b, id;
} q[N];
bool cmp(que a, que b) {
int x = a.l / len, y = b.l / len;
if (x == y) {
if (x & 1) return a.r < b.r;
return a.r > b.r;
}
return a.l < b.l;
}
vector<que> qb[W];
struct bitsett {
int siz;
vector<ull> v;
void reset() { for (int i = 0; i <= siz; i++) v[i] = 0; }
void set() { for (int i = 0; i <= siz; i++) v[i] = ~0ull; }
void set1(int x) { v[x >> bit] |= (1ull << (x & MOD)); }
void set0(int x) { v[x >> bit] &= (~(1ull << (x & MOD))); }
void flip() { for (int i = 0; i <= siz; i++) v[i] = ~v[i]; }
void operator &= (const bitsett &a) { for (int i = 0; i <= siz; i++) v[i] &= a.v[i]; }
int mex() { for (int i = 0; ; i++) if (~v[i]) return i << bit | __builtin_ctzll(~v[i]); }
void init(int x) {
v.resize((x >> bit) + 2);
siz = x >> bit;
reset();
}
bool any() {
for (int i = 0; i <= siz; i++) if (v[i]) return 1;
return 0;
}
};
bitsett cur, res, bl[1605], blb[W + 5];
void split(int len) {
for (int i = 0; i <= V / len + 2; i++) bl[i].init(len);
int ned = bl[0].siz, bg = 0, np = 0, num = 0;
for (int i = 0; i <= cur.siz; i++)
if (np == ned) {
if (bg + (len & MOD) <= MOD) {
bl[num].v[np] = (cur.v[i] & (filter[bg + (len & MOD) - 1] - (bg ? filter[bg - 1] : 0))) >> bg;
i--;
}
else if (i != cur.siz) {
bl[num].v[np] = (cur.v[i] >> bg) | ((cur.v[i + 1] & filter[bg + (len & MOD) - MOD]) << (W - bg));
} else
bl[num].v[np] = (cur.v[i] >> bg);
bg = (bg + (len & MOD)) & MOD, np = 0, num++;
} else {
if (bg == 0) bl[num].v[np] = cur.v[i];
else if (i != cur.siz) bl[num].v[np] = (cur.v[i] >> bg) | ((cur.v[i + 1] & filter[bg - 1]) << (W - bg));
else bl[num].v[np] = cur.v[i] >> bg;
np++;
}
}
void add1(int x) {
cnt[x]++;
if (cnt[x] == 1) cur.set1(x);
}
void del1(int x) {
cnt[x]--;
if (cnt[x] == 0) cur.set0(x);
}
void add2(int x) {
cnt[x]++;
if (cnt[x] == 1) blb[x % nowb].set1(x / nowb);
}
void del2(int x) {
cnt[x]--;
if (cnt[x] == 0) blb[x % nowb].set0(x / nowb);
}
int main() {
filter[0] = 1;
for (int i = 1; i < W; i++) filter[i] = filter[i - 1] + (1ull << i);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
cin >> m;
for (int i = 1, l, r, b; i <= m; i++) {
cin >> l >> r >> b;
if (b > 63) q[++cq] = (que){l, r, b, i};
else qb[b].pb((que){l, r, 0, i});
}
len = n / sqrt(cq) + 1;
sort(q + 1, q + cq + 1, cmp);
cur.init(V);
for (int i = 1, l = 1, r = 0; i <= cq; i++) {
while (l > q[i].l) add1(a[--l]);
while (r < q[i].r) add1(a[++r]);
while (l < q[i].l) del1(a[l++]);
while (r > q[i].r) del1(a[r--]);
split(q[i].b);
res.init(q[i].b);
res.flip();
for (int j = 0; ; j++) {
res &= bl[j];
if (!res.any()) {
ans[q[i].id] = j;
break;
}
}
}
int mxb = 63;
for (int i = 1; i <= mxb; i++) {
if (qb[i].size() == 0) continue;
memset(cnt, 0, sizeof(cnt));
nowb = i;
len = n / sqrt(qb[i].size()) + 1;
sort(qb[i].begin(), qb[i].end(), cmp);
for (int j = 0; j < i; j++) blb[j].init(V / i + 1);
for (int j = 0, l = 1, r = 0; j < (int)(qb[i].size()); j++) {
while (l > qb[i][j].l) add2(a[--l]);
while (r < qb[i][j].r) add2(a[++r]);
while (l < qb[i][j].l) del2(a[l++]);
while (r > qb[i][j].r) del2(a[r--]);
for (int k = 0; k < i; k++)
ans[qb[i][j].id] = max(ans[qb[i][j].id], blb[k].mex());
}
}
for (int i = 1; i <= m; i++) cout << ans[i] << '\n';
return 0;
}
9 P9410 『STA - R2』机场修建
Problem
有
会出现以下操作 / 查询共
1 x y
开通城市 和城市 之间的双向航班。2 l r a
城市 的人口数都 。3 x
如果所有能够到达城市 的人都来到城市 ,城市 有多少人。
Sol
感觉这种序列上各个部分不一定连续的查询以及区间加,对
下面是分块的题解(当然根号分治也可以做):
考虑稍微暴力的分块。区间加的话,散块部分可以直接加到全局的
占内存的是要维护每一块的
时间复杂度:
Code
// Problem:P9410
#include<bits/stdc++.h>
#define pb push_back
#define ios ios::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9, infi32 = 2147483647, mod = 1e9 + 7;
const ll INF = 1e18;
const int N = 2e5 + 10, B = 450 + 10;
int n, m, len, b;
int fa[N], sz[N], L[B], R[B], pos[N];
ll tg[B], sum[N], tmp[B];
vector<pair<int, ll> > v[N];
int find(int x) {
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
int main() {
// freopen("data.in", "r", stdin);
// freopen("myans.out", "w", stdout);
ios
cin >> n >> m;
for (int i = 1; i <= n; i++) {
fa[i] = i;
sz[i] = 1;
}
len = sqrt(n), b = n / len + (n % len != 0);
for (int i = 1; i <= b; i++) {
L[i] = (i - 1) * len + 1, R[i] = min(i * len, n);
for (int j = L[i]; j <= R[i]; j++) pos[j] = i;
}
for (int i = 1; i <= n; i++) v[i].pb(make_pair(pos[i], 1));
for (int t = 1, opt, x, y, z; t <= m; t++) {
cin >> opt >> x;
if (opt == 1) {
cin >> y;
x = find(x), y = find(y);
if (x == y) continue;
memset(tmp, 0, sizeof(tmp));
if (sz[x] > sz[y]) swap(x, y);
fa[x] = y, sz[y] += sz[x], sum[y] += sum[x];
for (int i = 0; i < v[x].size(); i++) tmp[v[x][i].first] += v[x][i].second;
for (int i = 0; i < v[y].size(); i++) tmp[v[y][i].first] += v[y][i].second;
v[x].clear(); v[y].clear();
for (int i = 1; i <= b; i++)
if (tmp[i])
v[y].pb(make_pair(i, tmp[i]));
} else if (opt == 2) {
cin >> y >> z;
int p = pos[x], q = pos[y];
if (p == q) {
for (int i = x; i <= y; i++) sum[find(i)] += z;
continue;
}
for (int i = x; i <= R[p]; i++) sum[find(i)] += z;
for (int i = L[q]; i <= y; i++) sum[find(i)] += z;
for (int i = p + 1; i < q; i++) tg[i] += z;
} else {
x = find(x);
ll res = sum[x];
for (int i = 0; i < v[x].size(); i++)
res += tg[v[x][i].first] * v[x][i].second;
cout << res << "\n";
}
}
return 0;
}
10 P7735 NOI2021 轻重边
Problem
给定一棵
- 给定两个点
,对于 路径上的所有点 ,将与其相连的边变为白色,然后将路径 染黑。 - 给定两个点
,求 路径经过的黑色边的数量。
Sol
还是有点智慧的?
发现如果要正常树剖(即在实质上更改每一条边的颜色,如用数据结构维护)的话,是需要同时维护 BFS 序和 DFS 序,这是无法做到的。考虑将边的颜色进行一个转化,即满足另外的某种条件使得可以判断边的颜色,且要能够方便修改与查询。
发现其实边的修改是很麻烦的,操作修改的都是边,而边又依赖于点,考虑以点的条件来判断边的颜色。那么边的颜色显然会取决于两边的点。如果只要一端(如钦定深度较小的点)是有问题的,因为
于是这道题就在
Code
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 2147483647, mod = 1e9 + 7;
const ll INF = 1e18;
const int N = 1e5 + 10;
int n, m, cnt, numc;
int son[N], fa[N], dfn[N], rdfn[N], top[N], siz[N], d[N];
int head[N], cnte;
struct edge {
int v, nxt;
} e[N << 1];
void adde(int u, int v) {
e[++cnte].v = v;
e[cnte].nxt = head[u];
head[u] = cnte;
}
struct node {
int s, lc, rc, tg;
friend bool operator != (node a, node b) {
return a.s != b.s || a.lc != b.lc || a.rc != b.rc || a.tg != b.tg;
}
friend bool operator == (node a, node b) {
return !(a != b);
}
} t[N << 2], unt;
struct segt {
void pushup(int u) {
t[u].s = t[u << 1].s + t[u << 1 | 1].s;
if (t[u << 1].rc == t[u << 1 | 1].lc) t[u].s++;
t[u].lc = t[u << 1].lc, t[u].rc = t[u << 1 | 1].rc;
}
void pushdown(int u, int l, int r) {
if (t[u].tg) {
int mid = l + r >> 1;
t[u << 1].tg = t[u << 1 | 1].tg = t[u].tg;
t[u << 1].lc = t[u << 1].rc = t[u << 1 | 1].lc = t[u << 1 | 1].rc = t[u].tg;
t[u << 1].s = mid - l, t[u << 1 | 1].s = r - mid - 1;
t[u].tg = 0;
}
}
void build(int u, int l, int r) {
if (l == r) {
t[u].lc = t[u].rc = ++numc;
t[u].s = 0;
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void update(int u, int l, int r, int L, int R, int col) {
if (L <= l && r <= R) {
t[u].tg = t[u].lc = t[u].rc = col;
t[u].s = r - l;
return;
}
pushdown(u, l, r);
int mid = l + r >> 1;
if (L <= mid) update(u << 1, l, mid, L, R, col);
if (mid < R) update(u << 1 | 1, mid + 1, r, L, R, col);
pushup(u);
}
node query(int u, int l, int r, int L, int R) {
if (L <= l && r <= R) return (node){t[u].s, t[u].lc, t[u].rc, 0};
pushdown(u, l, r);
int mid = l + r >> 1;
node ax = unt, ay = unt, res = unt;
if (L <= mid) ax = query(u << 1, l, mid, L, R);
if (mid < R) ay = query(u << 1 | 1, mid + 1, r, L, R);
if (ax == unt) return ay;
else if (ay == unt) return ax;
res.lc = ax.lc, res.rc = ay.rc;
res.s = ax.s + ay.s + (ax.rc == ay.lc);
return res;
}
} tr;
void dfs1(int u, int f) {
siz[u] = 1, fa[u] = f, d[u] = d[f] + 1;
int maxp = 0;
for (int i = head[u], v; i != 0; i = e[i].nxt)
if ((v = e[i].v) != f) {
dfs1(v, u);
siz[u] += siz[v];
if (siz[maxp] < siz[v]) maxp = v;
}
son[u] = maxp;
}
void dfs2(int u, int f) {
top[u] = f, dfn[u] = ++cnt, rdfn[cnt] = u;
if (!son[u]) return;
dfs2(son[u], f);
for (int i = head[u], v; i != 0; i = e[i].nxt)
if ((v = e[i].v) != fa[u] && v != son[u])
dfs2(v, v);
}
void upd(int u, int v, int col) {
while (top[u] != top[v]) {
if (d[top[u]] < d[top[v]]) swap(u, v);
tr.update(1, 1, n, dfn[top[u]], dfn[u], col);
u = fa[top[u]];
}
if (d[u] < d[v]) swap(u, v);
tr.update(1, 1, n, dfn[v], dfn[u], col);
}
int getlca(int u, int v) {
while (top[u] != top[v]) {
if (d[top[u]] < d[top[v]]) swap(u, v);
u = fa[top[u]];
}
if (d[u] < d[v]) swap(u, v);
return v;
}
int qry(int u, int v) {
int lcl = -1, res = 0, lca = getlca(u, v); node p;
while (top[u] != top[lca]) {
p = tr.query(1, 1, n, dfn[top[u]], dfn[u]);
res += p.s + (p.rc == lcl);
lcl = p.lc;
u = fa[top[u]];
}
p = tr.query(1, 1, n, dfn[lca], dfn[u]);
res += p.s + (p.rc == lcl);
lcl = -1;
while (top[v] != top[lca]) {
p = tr.query(1, 1, n, dfn[top[v]], dfn[v]);
res += p.s + (p.rc == lcl);
lcl = p.lc;
v = fa[top[v]];
}
p = tr.query(1, 1, n, dfn[lca], dfn[v]);
res += p.s + (p.rc == lcl);
return res;
}
void clr() {
memset(t, 0, sizeof(t));
memset(head, 0, sizeof(head));
memset(son, 0, sizeof(son));
memset(fa, 0, sizeof(fa));
memset(d, 0, sizeof(d));
memset(siz, 0, sizeof(siz));
memset(dfn, 0, sizeof(dfn));
memset(rdfn, 0, sizeof(rdfn));
memset(top, 0, sizeof(top));
memset(e, 0, sizeof(e));
cnte = cnt = numc = 0;
unt = (node){0, 0, 0, 0};
}
void solve() {
clr();
cin >> n >> m;
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v;
adde(u, v), adde(v, u);
}
dfs1(1, 0);
dfs2(1, 1);
tr.build(1, 1, n);
for (int i = 1, opt, u, v; i <= m; i++) {
cin >> opt >> u >> v;
if (opt == 1)
upd(u, v, ++numc);
else
cout << qry(u, v) << "\n";
}
}
int main() {
ios :: sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
11 P4689 [Ynoi2016] 这是我自己的发明
Problem
给定一棵
1 x
,将根节点换为 。2 x y
,给定两个点 ,求从 的子树中选一个点, 的子树中选一个点,点权相等的情况数。
Sol
首先换根操作肯定是假的,然后这个时候
实现上可以将前面最开始的
时间复杂度:
Code
// Problem:P4689
#include<bits/stdc++.h>
#define pb push_back
#define ios ios::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 2147483647, mod = 1e9 + 7;
const ll INF = 1e18;
const int N = 1e5 + 10, QN = 5e5 + 10;
int n, m, num, rt, cnt, cq, len, queq;
int a[N], b[N], t[N], dfn[N], rdfn[N << 1], siz[N], c1[N], c2[N], fa[N][20], d[N];
ll res, ans[QN];
int head[N], cnte;
struct edge {
int v, nxt;
} e[N << 1];
void adde(int u, int v) {
e[++cnte].v = v;
e[cnte].nxt = head[u];
head[u] = cnte;
}
struct que {
int l, r, typ, id;
} q[QN << 2];
bool cmp(que a, que b) {
int x = a.l / len, y = b.l / len;
if (x == y) {
if (x & 1) return a.r < b.r;
return a.r > b.r;
}
return a.l < b.l;
}
void dfs_init(int u, int f) {
dfn[u] = ++cnt, rdfn[cnt] = u, siz[u] = 1, fa[u][0] = f, d[u] = d[f] + 1;
for (int i = 1; i <= 18; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (int i = head[u], v; i != 0; i = e[i].nxt)
if ((v = e[i].v) != f) {
dfs_init(v, u);
siz[u] += siz[v];
}
}
int getnode(int u, int v) {
for (int i = 18; i >= 0; i--)
if (d[fa[u][i]] > d[v])
u = fa[u][i];
return u;
}
void add1(int x) { c1[x]++; res += c2[x]; }
void del1(int x) { c1[x]--; res -= c2[x]; }
void add2(int x) { c2[x]++; res += c1[x]; }
void del2(int x) { c2[x]--; res -= c1[x]; }
int main() {
// freopen("data.in", "r", stdin);
// freopen("myans.out", "w", stdout);
ios
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
b[i] = a[i];
}
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v;
adde(u, v), adde(v, u);
}
dfs_init(rt = 1, 0);
sort(b + 1, b + n + 1);
num = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(b + 1, b + num + 1, a[i]) - b;
rdfn[n + i] = rdfn[i];
}
for (int i = 1, opt, x, y; i <= m; i++) {
cin >> opt >> x;
if (opt == 1) rt = x;
else {
cin >> y; queq++;
int a, b, c, d;
if (rt == x) a = 1, b = n;
else if (dfn[x] < dfn[rt] && dfn[rt] <= dfn[x] + siz[x] - 1) {
int pos = getnode(rt, x);
a = dfn[pos] + siz[pos], b = n + dfn[pos] - 1;
} else a = dfn[x], b = dfn[x] + siz[x] - 1;
if (rt == y) c = 1, d = n;
else if (dfn[y] < dfn[rt] && dfn[rt] <= dfn[y] + siz[y] - 1) {
int pos = getnode(rt, y);
c = dfn[pos] + siz[pos], d = n + dfn[pos] - 1;
} else c = dfn[y], d = dfn[y] + siz[y] - 1;
if (a > c) swap(a, c), swap(b, d);
q[++cq] = (que){min(b, d), max(b, d), 1, queq};
q[++cq] = (que){min(a - 1, c - 1), max(a - 1, c - 1), 1, queq};
q[++cq] = (que){min(b, c - 1), max(b, c - 1), -1, queq};
q[++cq] = (que){min(d, a - 1), max(d, a - 1), -1, queq};
}
}
len = 2 * n / sqrt(cq) + 1;
sort(q + 1, q + cq + 1, cmp);
for (int i = 1, l = 0, r = 0; i <= cq; i++) {
while (l < q[i].l) add1(a[rdfn[++l]]);
while (r < q[i].r) add2(a[rdfn[++r]]);
while (l > q[i].l) del1(a[rdfn[l--]]);
while (r > q[i].r) del2(a[rdfn[r--]]);
ans[q[i].id] += q[i].typ * res;
}
for (int i = 1; i <= queq; i++) cout << ans[i] << "\n";
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!