2024.7.20 模拟赛总结
T1 lcd
Statement:
给定
Solution:
简单题。令
T2 cut
Statement:
给定一颗有
Solution:
比较简单的换根
先考虑如何求出整棵树的非空连通子图个数,
考虑切掉
qwq
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 5e5 + 10, mod = 998244353; struct edge{ int v, id, next; }edges[N << 1]; int head[N], idx; int f[N][2], g[N][2]; struct Edge{ int u, v, ansu, ansv; }ans[N]; vector<int> pre[N], suf[N]; int qpow(int x, int y){ int ret = 1; x %= mod; while(y){ if(y & 1) ret = ret * x % mod; x = x * x % mod; y >>= 1; } return ret; } void add_edge(int u, int v, int id){ edges[++idx] = {v, id, head[u]}; head[u] = idx; } int n; void dfs1(int u, int fa){ f[u][1] = 1; for(int i = head[u]; i; i = edges[i].next){ int v = edges[i].v; if(v == fa) continue; dfs1(v, u); f[u][0] = (f[u][0] + ((f[v][1] + f[v][0]) % mod)) % mod; f[u][1] = f[u][1] * ((1 + f[v][1]) % mod) % mod; pre[u].push_back(1 + f[v][1]); suf[u].push_back(1 + f[v][1]); } for(int i = suf[u].size() - 2; i >= 0; i--) suf[u][i] = (suf[u][i + 1] * suf[u][i]) % mod; for(int i = 1; i < suf[u].size(); i++) pre[u][i] = (pre[u][i - 1] * pre[u][i]) % mod; suf[u].push_back(1); // cout << u << " " << f[u][1] << " " << f[u][0] << "\n"; } void dfs2(int u, int fa, int id){ int f0 = (g[fa][0] - ((f[u][0] + f[u][1]) % mod) + mod) % mod, f1 = id; if(u != 1) g[u][0] = (f[u][0] + (f0 + f1)) % mod, g[u][1] = (f[u][1] * (1 + f1)) % mod; else g[u][1] = f[u][1], g[u][0] = f[u][0], f1 = 0; int tot = 0; for(int i = head[u]; i; i = edges[i].next){ int v = edges[i].v; if(v == fa) continue; int fu0 = (g[u][0] - ((f[v][0] + f[v][1]) % mod) + mod) % mod, fu1 = (((1 + f1) * (tot ? pre[u][tot - 1] : 1) % mod) * suf[u][tot + 1] % mod) % mod; int ansv = (f[v][1] + f[v][0]) % mod, ansu = (fu0 + fu1) % mod; // cout << u << " " << v << " " << f[u][0] << " " << f[u][1] << " " << f0 << " " << f1 << " " << fu0 << " " << fu1 << "\n"; if(u == ans[edges[i].id].u) ans[edges[i].id].ansu = ansu, ans[edges[i].id].ansv = ansv; else ans[edges[i].id].ansu = ansv, ans[edges[i].id].ansv = ansu; dfs2(v, u, fu1); tot++; } } signed main(){ //freopen("3.in", "r", stdin); //freopen("my.out", "w", stdout); ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n; for(int i = 1; i < n; i++){ int x, y; cin >> x >> y; add_edge(x, y, i); add_edge(y, x, i); ans[i] = {x, y, 0, 0}; } dfs1(1, 0); dfs2(1, 0, 0); for(int i = 1; i < n; i++) cout << ans[i].ansu << " " << ans[i].ansv << "\n"; //system("pause"); return 0; } /* 5 1 2 2 3 3 4 4 5 */
T3 为了你唱下去
Statement:
给定一张有
Solution:
图论好题。
首先容易发现两个关键点能够相互到达当且仅当两个点的最短距离小于等于
考虑优化合并集合的过程。我们首先将所有关键点的距离标记为
接着我们将所有所在集合大小大于等于
qwq
#include<bits/stdc++.h> #define int long long #define pir pair<int, int> using namespace std; const int N = 1e5 + 10, M = 5e5 + 10, INF = 1e18; int n, m, c, r, k, fa[N], siz[N], dis[N], vis[N], from[N]; struct edge{ int v, w, next; }edges[M << 1]; int head[N], idx; void add_edge(int u, int v, int w){ edges[++idx] = {v, w, head[u]}; head[u] = idx; } int findfa(int x){return fa[x] = ((x == fa[x]) ? x : findfa(fa[x]));} void merge(int x, int y){ int fx = findfa(x), fy = findfa(y); if(fx != fy){fa[fx] = fy; siz[fy] += siz[fx];} } void buildc(){ for(int i = 1; i <= n; i++) dis[i] = INF; priority_queue<pir, vector<pir>, greater<pir> >Q; for(int i = 1; i <= c; i++) dis[i] = 0, Q.push({0, i}), from[i] = i; while(!Q.empty()){ pir p = Q.top(); Q.pop(); int u = p.second, dist = p.first; if(vis[u]) continue; vis[u] = true; for(int i = head[u]; i; i = edges[i].next){ int v = edges[i].v; if((!vis[v]) && dis[v] > dist + edges[i].w && dist + edges[i].w <= r){ dis[v] = dist + edges[i].w; from[v] = from[u]; // cout << v << " " << u << " " << dist + edges[i].w << "\n"; Q.push({dis[v], v}); } } } for(int u = 1; u <= n; u++){ for(int i = head[u]; i; i = edges[i].next){ int v = edges[i].v; if(dis[v] + edges[i].w + dis[u] <= r) merge(from[u], from[v]); } } } void getans(){ for(int i = 1; i <= n; i++) dis[i] = INF, vis[i] = false; priority_queue<pir, vector<pir>, greater<pir> >Q; for(int i = 1; i <= c; i++) if(siz[findfa(i)] >= k) dis[i] = 0, Q.push({0, i}); // for(int i = 1; i <= c; i++) cout << siz[findfa(i)] << "\n"; while(!Q.empty()){ pir p = Q.top(); Q.pop(); int u = p.second, dist = p.first; if(vis[u]) continue; vis[u] = true; for(int i = head[u]; i; i = edges[i].next){ int v = edges[i].v; if(edges[i].w > r) continue; if((!vis[v]) && dis[v] > dist + edges[i].w){ dis[v] = dist + edges[i].w; Q.push({dis[v], v}); } } } int ans = 0; for(int i = c + 1; i <= n; i++) if(dis[i] <= r) ans++; cout << ans << "\n"; for(int i = c + 1; i <= n; i++) if(dis[i] <= r) cout << i << " "; } signed main(){ //freopen("ex_sample8.in", "r", stdin); //freopen("my.out", "w", stdout); ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> m >> c >> r >> k; for(int i = 1; i <= m; i++){ int x, y, w; cin >> x >> y >> w; add_edge(x, y, w); add_edge(y, x, w); } for(int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1; buildc(); getans(); return 0; }
T4 入学
Statement:
略。
Solution:
DS 好题。
首先发现题目中的等价于是对于序列套序列的维护,这是困难的。考虑优化掉一维,注意到查询只是单点查询,因此我们考虑分别计算每个点的询问答案。于是先将询问和操作进行离线,并维护当前点
现在处理对于队列
现在我们进而考虑操作二,操作二可能会在一些时间戳上清空整个序列,这是我们需要处理的。我们定义对于时间戳
qwq
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 3e5 + 10; namespace Addtree{ #define ls (o << 1) #define rs (o << 1 | 1) #define mid ((l + r) >> 1) int tsum[N << 2], lby[N << 2]; void pushup(int o){tsum[o] = tsum[ls] + tsum[rs];} void add(int o, int l, int r, int pos, int k, int bel){ if(l == r){ tsum[o] += k; lby[o] = bel; return; } if(pos <= mid) add(ls, l, mid, pos, k, bel); else add(rs, mid + 1, r, pos, k, bel); pushup(o); } int qrysum(int o, int l, int r, int R){ if(r <= R) return tsum[o]; if(R <= mid) return qrysum(ls, l, mid, R); else return tsum[ls] + qrysum(rs, mid + 1, r, R); } int qrypos(int o, int l, int r, int val){ if(l == r) return lby[o]; if(tsum[ls] >= val) return qrypos(ls, l, mid, val); else return qrypos(rs, mid + 1, r, val - tsum[ls]); } } namespace Mintree{ int tmin[N << 2], tag[N << 2]; void pushup(int o){tmin[o] = min(tmin[ls], tmin[rs]);} void addtagM(int o, int v){tmin[o] += v; tag[o] += v;} void pushdown(int o){ if(!tag[o]) return; addtagM(ls, tag[o]); addtagM(rs, tag[o]); tag[o] = 0; } void add(int o, int l, int r, int pos, int k){ if(l >= pos){addtagM(o, k); return;} pushdown(o); if(pos <= mid) add(ls, l, mid, pos, k), addtagM(rs, k); else add(rs, mid + 1, r, pos, k); pushup(o); } int qrymin(int o, int l, int r, int R){ if(r <= R) return tmin[o]; pushdown(o); if(R <= mid) return qrymin(ls, l, mid, R); else return min(tmin[ls], qrymin(rs, mid + 1, r, R)); } int qrySingle(int o, int l, int r, int pos){ if(l == r) return tmin[o]; pushdown(o); if(pos <= mid) return qrySingle(ls, l, mid, pos); else return qrySingle(rs, mid + 1, r, pos); } } int n, m, q, tot, ans[N]; struct query{ int id, cnt, bel; }; vector<query> Mo1[N], Mo2[N], qry[N]; signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> m >> q; for(int i = 1; i <= q; i++){ int opt, a, b, v1, v2; cin >> opt >> a >> b; if(opt == 1){ cin >> v1 >> v2; Mo1[a].push_back({i, v2, v1}); Mo1[b + 1].push_back({i, -v2, v1}); } if(opt == 2){ cin >> v1; Mo2[a].push_back({i, -v1, 0}); Mo2[b + 1].push_back({i, v1, 0}); } if(opt == 3){ qry[a].push_back({i, b, ++tot}); } } for(int tim = 1; tim <= n; tim++){ for(int i = 0; i < Mo1[tim].size(); i++){ query Q = Mo1[tim][i]; Addtree::add(1, 1, q, Q.id, Q.cnt, Q.bel); Mintree::add(1, 1, q, Q.id, Q.cnt); } for(int i = 0; i < Mo2[tim].size(); i++){ query Q = Mo2[tim][i]; Mintree::add(1, 1, q, Q.id, Q.cnt); } for(int i = 0; i < qry[tim].size(); i++){ query Q = qry[tim][i]; int sum = Addtree::qrysum(1, 1, q, Q.id), pre = min(0ll, Mintree::qrymin(1, 1, q, Q.id)), ths = Mintree::qrySingle(1, 1, q, Q.id); if(Q.cnt <= ths - pre) ans[Q.bel] = Addtree::qrypos(1, 1, q, sum - (ths - pre) + Q.cnt); } } for(int i = 1; i <= tot; i++) cout << ans[i] << "\n"; return 0; }
本文作者:Little_corn
本文链接:https://www.cnblogs.com/little-corn/p/18313700
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步