洛谷 P3931 SAC E#1 - 一道难题 Tree
洛谷 P3931 SAC E#1 - 一道难题 Tree
题目背景
冴月麟和魏潇承是好朋友。
题目描述
冴月麟为了守护幻想乡,而制造了幻想乡的倒影,将真实的幻想乡封印了。任何人都无法进入真实的幻想乡了,但是她给前来救她的魏潇承留了一个线索。
她设置了一棵树(有根)。树的每一条边上具有割掉该边的代价。
魏潇承需要计算出割开这棵树的最小代价,这就是冴月麟和魏潇承约定的小秘密。
帮帮魏潇承吧。
注:所谓割开一棵有根树,就是删除若干条边,使得任何任何叶子节点和根节点不连通。
输入格式
输入第一行两个整数n,S表示树的节点个数和根。
接下来n-1行每行三个整数a、b、c,表示a、b之间有一条代价为c的边。
输出格式
输出包含一行,一个整数,表示所求最小代价。
题解:
题意就很像最小割裸题。但是最小割只要求汇点与源点不联通,这道题要求所有叶子节点都不连通。很容易啊,建超级汇点就好了。
然后根据最大流最小割定理,直接在上面跑Dinic最大流。
代码:(不知道为什么,这份代码会MLE一半的点,如有大佬请指正)
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=1e5+5;
const int INF=1e9;
int n,s,t,ans;
int tot=1,head[maxn],nxt[maxn<<1],to[maxn<<1],val[maxn<<1];
int lv[maxn],cur[maxn];
bool v[maxn];
queue<int> q;
void add(int x,int y,int z)
{
to[++tot]=y;
nxt[tot]=head[x];
val[tot]=z;
head[x]=tot;
}
void dfs_pre(int x,int f)
{
bool flag=0;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)
continue;
flag=1;
dfs_pre(y,x);
val[i^1]=0;
}
if(!flag)
{
add(x,t,INF);
add(t,x,0);
}
}
bool bfs()
{
memset(lv,0,sizeof(lv));
lv[s]=1;
q.push(s);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(!val[i]||lv[y])
continue;
lv[y]=lv[x]+1;
q.push(y);
}
}
return lv[t];
}
int dfs(int x,int flow)
{
if(!flow||x==t)
return flow;
int re=0;
for(int i=cur[x];i;i=nxt[i])
{
cur[x]=i;
int y=to[i];
if(val[i]>0 && lv[y]==lv[x]+1)
{
int tmp=dfs(y,min(val[i],flow));
if(!tmp)
continue;
flow-=tmp;
re+=tmp;
val[i]-=tmp;
val[i^1]+=tmp;
}
}
return re;
}
int main()
{
scanf("%d%d",&n,&s);
t=n+1;
for(int i=1;i<n;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
dfs_pre(s,-1);
while(bfs())
{
memcpy(cur,head,sizeof(head));
ans+=dfs(s,INF);
}
printf("%d\n",ans);
return 0;
}