LOJ 2737 「JOISC 2016 Day 3」电报 ——思路+基环树DP
题目:https://loj.ac/problem/2737
相连的关系形成若干环 / 内向基环树 。如果不是只有一个环的话,就得断开一些边使得图变成若干链。边的边权是以它为出边的点的点权。
基环树的树的部分可以 DP 或者贪心,贪心就是只在分叉处断边。
对于每个环,先做出 f[ i ][ 0/1 ] 表示环上这个点 不向下 / 向下 延伸链的代价,然后在环上 DP 。
方法就是先指定 tot -> 1 的边不选, DP 一番,再制定 tot -> 1 的边选, DP 一番。
如果指定 tot -> 1 的边选,要注意不能连出一个环。所以不仅有 g[ i ][ 0/1 ] 表示 “可以向右延伸” / “无要求” , 还要有一个 [ 0/1 ] 表示 “之前有没有断过” 。不过不用有 g[ i ][ 1 ][ 1 ] ,因为 “无要求” 就是表示 i -> i+1 得断开,这样就一定 “断过” 了;所以就记 g[ i ][ 2 ] 表示 “之前断过,现在可以向右延伸” 。 g[ tot ][ 2 ] 就是指定 tot -> 1 的边选的时候加给答案的贡献。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } ll Mn(ll a,ll b){return a<b?a:b;} ll Mx(ll a,ll b){return a>b?a:b;} const int N=1e5+5; const ll INF=1e14+5; int n,hd[N],xnt,to[N<<1],nxt[N<<1],w[N<<1],tp[N],tw[N],tot; int cd[N],cnt,col[N],tim,dfn[N],low[N],sta[N],top; ll ans,f[N][2],g[N][3]; bool vis[N],ins[N]; void add(int x,int y,int z) { to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;w[xnt]=z;cd[x]++; } void tarjan(int cr) { dfn[cr]=low[cr]=++tim; sta[++top]=cr; ins[cr]=1; for(int i=hd[cr],v;i;i=nxt[i]) if(!dfn[v=to[i]])tarjan(v),low[cr]=Mn(low[cr],low[v]); else if(ins[v])low[cr]=Mn(low[cr],dfn[v]); if(dfn[cr]==low[cr]) { cnt++; int siz=0; do{ ins[sta[top]]=0; col[sta[top]]=cnt; siz++; }while(sta[top--]!=cr); if(siz==1)col[cr]=0, cnt--; } } void dfs(int cr) { ins[cr]=1; for(int i=hd[cr],v;i;i=nxt[i]) if(!col[v=to[i]]) { dfs(v=to[i]); ll t0=f[cr][0]+f[v][1]+w[i]; ll t1=Mn(f[cr][0]+f[v][1],f[cr][1]+f[v][1]+w[i]); f[cr][0]=t0; f[cr][1]=t1; } } void ini_dfs(int cr,int st) { dfs(cr); tp[++tot]=cr; for(int i=hd[cr],v;i;i=nxt[i]) if(col[v=to[i]]) {tw[tot]=w[i]; if(v!=st)ini_dfs(v,st);} } void solve(int cr) { tot=0; ini_dfs(cr,cr); g[1][0]=tw[tot]+f[tp[1]][0]; g[1][1]=tw[tot]+f[tp[1]][1];//f[tp[]]!! for(int i=2;i<=tot;i++) { int cr=tp[i];// g[i][0]=Mn(g[i-1][0],g[i-1][1]+tw[i-1])+f[cr][0]; g[i][1]=Mn(g[i-1][0],g[i-1][1]+tw[i-1])+f[cr][1]; } ll ret=g[tot][1]; g[1][0]=f[tp[1]][0]; g[1][1]=f[tp[1]][1]; g[1][2]=g[1][3]=INF; for(int i=2;i<=tot;i++) { int cr=tp[i]; g[i][0]=Mn(g[i-1][0],g[i-1][1]+tw[i-1])+f[cr][0]; g[i][1]=Mn(g[i-1][0],g[i-1][1]+tw[i-1])+f[cr][1]; g[i][2]=Mn(g[i-1][2],g[i-1][1]+tw[i-1])+f[cr][0]; } ans+=Mn(ret,g[tot][2]); } int main() { n=rdn(); for(int i=1,d,z;i<=n;i++) { d=rdn(); z=rdn(); add(d,i,z);} bool fg=0;for(int i=1;i<=n;i++)if(cd[i]!=1){fg=1;break;} if(!fg) { int cr=1;while(!vis[cr]){vis[cr]=1;cr=to[hd[cr]];} for(int i=1;i<=n;i++)if(!vis[i]){fg=1;break;} if(!fg){puts("0");return 0;} } for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i); for(int i=1;i<=n;i++) if(!ins[i]&&col[i])solve(i); printf("%lld\n",ans); return 0; }