【题解】【CTST2010】星际旅行
看到这道题第一眼是什么?
网络流!exciting!
大佬们以光速复制敲好板子,用3毫秒想出了如何建模,打好程序,看眼数据范围……
笑容渐渐消失.jpg
什么,还有我预流推进解决不了的卡常网络流?
还真搞不定
再仔细看一眼:N个星球,N-1条双向时空隧道,任意两点联通
——这是树!
树上网络流不行,那么……还有什么算法可以解决这类问题……
——树上DP!
好了怎么DP也是一个问题啊
先看一句话:
每个星球的H_iHi大于等于与该星球直接相连的星球数(即度数)。
什么鬼?为什么会有这种毒瘤玩意儿?
其实ta给我们提供了一个很关键的信息:
由于有Hi>=i的度数,那么可以想象在最坏情况下(比如所有Hi=i的度数),答案至少是根节点走过所有点之后回到根节点,然后去掉一个点i到根节点的那些路径。
——来自https://blog.csdn.net/lych_cys/article/details/51062609
那么感性一下,答案大概就是根节点回到根节点的最大流量,使得没有一条边e(u,v),u和v的可出发次数都>0。
分三种情况讨论:
- 还可以从x处出发,总流量++,cnt[x]--
- y而儿子中存在一个点p的可出发次数>0,那么可以让y->x的流量-1,然后y->p流量+1,p->y流量+1,p的可出发次数-1;
- 否则没有办法到达y,那么只能让y->x的边退流,同时y的可出发次数+1。
——仍然来自https://blog.csdn.net/lych_cys/article/details/51062609
那么……
这就很显然了,代码就不防了(被泼硫酸
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
/*
inline void read(int &x)
{
x=0;
int f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
{
f=-1;
}
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
x*=f;
}
*/
struct note
{
int next;
int t;
};
note e[200010];
int head[50010],ind[50010];
int n;
int cnt;
int flag[50010],ans[50010],g[50010];
int flow;
inline void add(int x,int y)
{
e[++cnt].t=y;
e[cnt].next=head[x];
head[x]=cnt;
ind[y]--;
}
void dfs(int x,int fa)
{
for(int i=head[x];i+1;i=e[i].next)
{
int p=e[i].t;
if(p==fa)
{
continue;
}
dfs(p,x);
int val=min(ind[p],ind[x]);
ind[x]-=val;
ind[p]-=val;
flow+=val<<1;
if(ind[p])
{
g[x]=p;
}
}
}
void solve(int x,int fa)
{
ans[x]=flow;
for(int i=head[x];i+1;i=e[i].next)
{
int p=e[i].t;
if(p==fa)
{
continue;
}
if(ind[x])
{
ind[x]--;
flow++;
flag[x]=0;
}
else if(g[p])
{
ind[g[p]]--;
flow++;
flag[x]=1;
}
else
{
ind[p]++;
flow--;
flag[x]=2;
}
solve(p,x);
if(!flag[x])
{
ind[x]++;
flow--;
}
else if(flag[x]==1)
{
ind[g[p]]++;
flow--;
}
else
{
ind[p]--;
flow++;
}
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&ind[i]);
}
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
x++;
y++;
add(x,y);
add(y,x);
}
flow=(n-1)<<1;
dfs(1,-1);
solve(1,-1);
for(int i=1;i<=n;i++)
{
printf("%d\n",ans[i]);
}
return 0;
}