题解:P5680 [GZOI2017] 共享单车
题目分析
出题人是擅长隐藏题意的
建树
首先给你一张无向图,然后指定一个根节点
考虑记录前驱的 Dijkstra。
namespace DJ { int dis[maxn], pre[maxn], val[maxn], vis[maxn]; priority_queue<pair<int, int>> pq; void Dijkstra(int s) { memset(dis, 0x3f, sizeof dis); dis[s]=0; pre[s]=0; pq.emplace(0, s); while(!pq.empty()) { auto [d, u]=pq.top(); pq.pop(); if(vis[u]) continue; vis[u]=1; for(auto [v, w]:e[u]) { if(dis[u]+w<dis[v]) { dis[v]=dis[u]+w; pre[v]=u; val[v]=w; pq.emplace(-dis[v], v); } else if(dis[u]+w==dis[v]) pre[v]=min(pre[v], u), val[v]=w; } } } void search(int x) // 记录路径 { while(vis[x]&&pre[x]) { e[pre[x]].emplace_back(x, val[x]); vis[x]=0; x=pre[x]; } } }
询问
首先用
对于 0
操作,就是翻转点的标记。
if(op==0) for(int i=1, t;i<=num;i++) cin>>t, tag[t]^=1;
对于 1
操作,题目说的很清楚,先以
前置知识:虚树。
d.clear(); d.push_back(k); for(int i=1, t;i<=num;i++) cin>>t, d.emplace_back(t); sort(d.begin(), d.end(), cmp); for(int i=1;i<=num;i++) d.push_back(LCA(d[i], d[i-1])); sort(d.begin(), d.end(), cmp); auto end_it=unique(d.begin(), d.end()); for(auto it=d.begin()+1;it!=end_it;it++) { int lc=LCA(*(it-1), *it); g[lc].emplace_back(*it, dis[*it]-dis[lc]); }
然后要求让根节点和每个有标记的节点均不连通,所以考虑虚树上树形dp。
令
易得转移方程:
如果
如果
int dp(int x) { int ret=0; for(auto [v, w]:g[x]) { int f=dp(v); if(tag[v]) ret+=w; else ret+=min(w, f); } g[x].clear(); return ret; }
因为边权不为 -1
。
Code
#include<bits/stdc++.h> using namespace std; #define maxn 50004 vector<pair<int, int>> e[maxn], g[maxn]; namespace DJ { int dis[maxn], pre[maxn], val[maxn], vis[maxn]; priority_queue<pair<int, int>> pq; void Dijkstra(int s) { memset(dis, 0x3f, sizeof dis); dis[s]=0; pre[s]=0; pq.emplace(0, s); while(!pq.empty()) { auto [d, u]=pq.top(); pq.pop(); if(vis[u]) continue; vis[u]=1; for(auto [v, w]:e[u]) { if(dis[u]+w<dis[v]) { dis[v]=dis[u]+w; pre[v]=u; val[v]=w; pq.emplace(-dis[v], v); } else if(dis[u]+w==dis[v]) pre[v]=min(pre[v], u), val[v]=w; } } } void search(int x) { while(vis[x]&&pre[x]) { e[pre[x]].emplace_back(x, val[x]); vis[x]=0; x=pre[x]; } } } namespace slpf { int son[maxn], fa[maxn], siz[maxn], dep[maxn], dis[maxn]; void dfs1(int u, int f) { fa[u]=f; siz[u]=1; dep[u]=dep[f]+1; for(auto [v, w]:e[u]) if(v!=f) { dis[v]=dis[u]+w; dfs1(v, u); siz[u]+=siz[v]; if(siz[v]>siz[son[u]]) son[u]=v; } } int top[maxn], dfn[maxn]; void dfs2(int u, int t) { dfn[u]=++*dfn; top[u]=t; if(!son[u]) return; dfs2(son[u], t); for(auto [v, w]:e[u]) if(!dfn[v]) dfs2(v, v); } int LCA(int x, int y) { while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x, y); x=fa[top[x]]; } return dep[x]<dep[y]?x:y; } } using slpf::dfn; using slpf::dis; using slpf::LCA; int tag[maxn]; vector<int> d; int dp(int x) { int ret=0; for(auto [v, w]:g[x]) { int f=dp(v); if(tag[v]) ret+=w; else ret+=min(w, f); } g[x].clear(); return ret; } int main() { int n, m, k, q; cin>>n>>m>>k>>q; for(int i=1, u, v, w;i<=m;i++) { cin>>u>>v>>w; e[u].emplace_back(v, w); e[v].emplace_back(u, w); } DJ::Dijkstra(k); for(int i=1;i<=n;i++) e[i].clear(); for(int i=1;i<=n;i++) DJ::search(i); auto cmp=[&](int a, int b){return dfn[a]<dfn[b];}; slpf::dfs1(k, 0); slpf::dfs2(k, k); while(q--) { int op, num; cin>>op>>num; if(op==0) for(int i=1, t;i<=num;i++) cin>>t, tag[t]^=1; else { d.clear(); d.push_back(k); for(int i=1, t;i<=num;i++) cin>>t, d.emplace_back(t); sort(d.begin(), d.end(), cmp); for(int i=1;i<=num;i++) d.push_back(LCA(d[i], d[i-1])); sort(d.begin(), d.end(), cmp); auto end_it=unique(d.begin(), d.end()); for(auto it=d.begin()+1;it!=end_it;it++) { int lc=LCA(*(it-1), *it); g[lc].emplace_back(*it, dis[*it]-dis[lc]); } int otp=dp(k); cout<<(otp?otp:-1)<<'\n'; } } }
本文作者:Jimmy-LEEE
本文链接:https://www.cnblogs.com/redacted-area/p/18379514
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步