NOIP 模擬 $13\; \text{卡常題}$
阿新 • • 發佈:2021-07-13
題解
題解
一道環套樹的最小點覆蓋題目,所謂環套樹就是有在 \(n\) 個點 \(n\) 條邊的無向聯通圖中存在一個環
我們可以發現其去掉一條環上的邊後就是一棵樹
那麼對於此題,我們把所有 \(x\) 方點當點 \(y\) 方點當邊,隨便找一條環上的邊刪掉,然後分別從此邊的兩個端點做樹形 \(dp\)
對於一條邊上的兩個點,我們一定要選一個,但不需要都選,類似例題
所以方程很好推,\(dp_{i,0}\) 表示不選 \(i\) 後覆蓋 \(i\) 子樹的最小費用,\(dp_{i,0}\) 表示選 \(i\) 後覆蓋 \(i\) 子樹的最小費用
\[dp_{x,0}=\sum_v^{v\in son_x}dp_{v,1} \]\[dp_{x,1}=\sum_v^{v\in son_x}\min(dp_{v,0},dp_{v,1}) \]最後取兩個端點中值最小的,因為我們也要覆蓋被斷開的邊,所以端點必須要選取一個
Code
#include<bits/stdc++.h> #define ri register signed #define p(i) ++i using namespace std; namespace IO{ char buf[1<<21],*p1=buf,*p2=buf; #define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++ template<typename T>inline void read(T &x) { ri f=1;x=0;register char ch=gc(); while(ch<'0'||ch>'9') {if (ch=='-') f=0;ch=gc();} while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=gc();} x=f?x:-x; } } using IO::read; namespace nanfeng{ #define cmax(x,y) ((x)>(y)?(x):(y)) #define cmin(x,y) ((x)>(y)?(y):(x)) #define FI FILE *IN #define FO FILE *OUT static const int N=1e6+7; int dp[N][2],first[N],cst[N],vis[N],bk1,bk2,t=1,n,a,b; struct edge{int v,nxt;}e[N<<1]; inline void add(int u,int v) { e[t].v=v,e[t].nxt=first[u],first[u]=t++; e[t].v=u,e[t].nxt=first[v],first[v]=t++; } void dfs_pre(int x,int fa) { if (vis[x]) {bk1=x,bk2=fa;return;} vis[x]=1; for (ri i(first[x]),v;i;i=e[i].nxt) { if ((v=e[i].v)==fa) continue; dfs_pre(v,x); } } void dfs(int x,int fa) { dp[x][0]=0,dp[x][1]=cst[x]; for (ri i(first[x]),v;i;i=e[i].nxt) { if ((v=e[i].v)==fa||x==bk1&&v==bk2||x==bk2&&v==bk1) continue; dfs(v,x); dp[x][0]+=dp[v][1]; dp[x][1]+=cmin(dp[v][1],dp[v][0]); } } inline int main() { // FI=freopen("nanfeng.in","r",stdin); // FO=freopen("nanfeng.out","w",stdout); read(n),read(a),read(b); for (ri i(1),v1,v2;i<=n;p(i)) { read(v1),read(v2); cst[v1]+=a,cst[v2]+=b; add(v1,v2); } dfs_pre(1,0),dfs(bk1,0); ri tmp=dp[bk1][1]; dfs(bk2,0); tmp=cmin(tmp,dp[bk2][1]); printf("%d\n",tmp); return 0; } } int main() {return nanfeng::main();}