【BZOJ】3302: [Shoi2005]树的双中心 && 2103: Fire 消防站 && 2447: 消防站
Description
Input
第一行为N,1<N<=50000,表示树的节点数目,树的节点从1到N编号。
接下来N-1行,每行两个整数U,V,表示U与V之间有一条边。
再接下N行,每行一个正整数,其中第i行的正整数表示编号为i的节点权值为W(I),树的深度<=100
Output
将最小的S(x,y)输出,结果保证不超过19^9
Sample Input
5
1 2
1 3
3 4
3 5
5
7
6
5
4
1 2
1 3
3 4
3 5
5
7
6
5
4
Sample Output
14
HINT
选取两个中心节点为2,3
—————————————————————————————
这道题很像树的重心只不过这里要求的重心有两个QAQ
考虑树的深度比较小 我们正常求树的重心的方法就是树的高度h
这样算下来我们因为要求两个重心 那么我们可以O(n)枚举一条边把树变成两个
再O(h)算答案 这样复杂度是可以接受的
注意我们本来的sz指的是子树内的权值和
枚举两个点之后两棵树一开始的重心默认为1棵 是1 1棵是u
然后在贪心往下找
当然这样可能会有问题那就是因为算的是min(dis[x][u],dis[x][v])
这样的话我们本来是贪心树里面的到这个点 但是这样肯定会错
这样的话其实也影响答案 因为这个只会使当前解比真实解大
最优解的情况是不会受影响的 这样就可以解决这道问题辣
tips sum+=sz[x](x->2-n)
这样其实就是在算路径和 这个转换使代码异常优美2333
#include<cstdio> #include<cstring> #include<algorithm> #define LL long long using std::min; using std::swap; const int M=1e5+7; const LL inf=0x3f3f3f3f; char buf[M*33],*ptr=buf-1; int read(){ int ans=0,f=1,c=*++ptr; while(c<'0'||c>'9'){if(c=='-') f=-1; c=*++ptr;} while(c>='0'&&c<='9'){ans=ans*10+(c-'0'); c=*++ptr;} return ans*f; } LL ans=inf,sz[M],mx[M],sec[M],sum,w[M]; int T,n,fa[M],dep[M]; int first[M],cnt; struct node{int from,to,next;}e[2*M]; void ins(int a,int b){e[++cnt]=(node){a,b,first[a]}; first[a]=cnt;} void insert(int a,int b){ins(a,b); ins(b,a);} void dfs(int x,int f){ sz[x]=w[x]; mx[x]=sec[x]=0; for(int i=first[x];i;i=e[i].next){ int now=e[i].to; if(now==f) continue; fa[now]=x; dep[now]=dep[x]+1; dfs(now,x); sz[x]+=sz[now]; if(sz[now]>sz[mx[x]]) sec[x]=mx[x],mx[x]=now; else if(sz[now]>sz[sec[x]]) sec[x]=now; } } int main(){ fread(buf,1,sizeof(buf),stdin); int x,y; n=read(); for(int i=1;i<n;i++) x=read(),y=read(),insert(x,y); for(int i=1;i<=n;i++) w[i]=read(); dep[1]=1; dfs(1,-1); sum=0; for(int i=2;i<=n;i++) sum+=sz[i]; for(int i=1;i<=cnt;i+=2){ int u=e[i].from,v=e[i].to; if(dep[u]<dep[v]) swap(u,v); LL nowh=sum; for(x=v;x;x=fa[x]) sz[x]-=sz[u],nowh-=sz[u]; x=1; while(1){ if(sz[mx[x]]>sz[sec[x]]&&mx[x]!=u) y=mx[x]; else y=sec[x]; if(sz[y]*2>sz[1]) x=y,nowh=nowh-sz[y]*2+sz[1]; else break; } x=u; while(1){ if(sz[mx[x]]*2>sz[u]) nowh=nowh-sz[mx[x]]*2+sz[u],x=mx[x]; else break; } ans=min(ans,nowh); for(x=v;x;x=fa[x]) sz[x]+=sz[u]; } printf("%lld\n",ans); return 0; }