题面

一句话题意:

给定一张 N 个点, M 条边的无向连通图, 每条边上有边权 w .

求删去任意一个点后的最小生成树的边权之和.

思路

首先肯定要$kruskal$一下

考虑$MST$里面去掉一个点,得到一堆联通块,我们要做的就是用原图中剩下的边把这些联通块穿起来

考虑这个点$u$在$MST$上的位置,可以知道有两种边:一种是从$u$的任意一个儿子的子树连到$u$的子树外面的,一种是在$u$的两个儿子的子树之间连接的

第一种情况:

考虑边$(u,v)$,没有进入$MST$中,那么若它是某个节点$x$从子树内连到子树外的一条边,x会在哪里呢?

显然,对于$u->lca->v$的这个链上,$x$可以是除了$u,v,lca$这三个点以外的任何一个

第二种情况:

还是考虑$(u,v)$和$x$,可以发现,此时$x$一定是$u$和$v$的$lca$

对于去掉一个点的问题而言,我们只需要保存去掉了它以后的每个联通块向外连出去的最小的一条边就可以了【这个很显然,自己想】

那么我们可以通过树链剖分+线段树,以及用一次$dfs$来询问、$dfs$过程中合并子树信息的方式,来维护上面两种情况的边,得到每个点删掉以后得到的联通块出来的最小边,再对它们跑$kruskal$

注意这样做的复杂度是对的,因为每个$MST$边都会对两个点的询问做出1的贡献,询问完$MST$中所有的点的过程中做$kruskal$的总点数是$2\ast n$

总复杂度应该是$O((m-n)\log^2n+n\log n)$的样子,还是可以接受的

注意这道题的细节非常多,写的时候一定要静态查错!

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#include<vector>
#define ll long long
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') flag=-1;
ch=getchar();
}
while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
int n,m,base;
int dep[100010],siz[100010],son[100010],fa[100010],dfn[100010],back[1000010],clk,top[100010],re[100010],st[100010][20];
namespace ufs{
int f[100010];
void init(int s){for(register int i=1;i<=s;i++) f[i]=-1;}
inline int find(int x){return ((f[x]<0)?x:(f[x]=find(f[x])));}
inline int join(int x,int y){
x=find(x);y=find(y);
if(x==y) return 0;
f[y]+=f[x];f[x]=y;return -f[y];
}
}
namespace seg{//线段树
int seg[400010];
void build(int l,int r,int num){
seg[num]=1e9;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,num<<1);build(mid+1,r,num<<1|1);
}
void change(int l,int r,int ql,int qr,int num,int val){
if(l>=ql&&r<=qr){seg[num]=min(seg[num],val);return;}
int mid=(l+r)>>1;
if(mid>=ql) change(l,mid,ql,qr,num<<1,val);
if(mid<qr) change(mid+1,r,ql,qr,num<<1|1,val);
}
int query(int l,int r,int pos,int num){
if(l==r) return seg[num];
int mid=(l+r)>>1;
if(mid>=pos) return min(query(l,mid,pos,num<<1),seg[num]);
else return min(query(mid+1,r,pos,num<<1|1),seg[num]);
}
}
namespace t{//这是MST树,上面有倍增、树剖等操作
int first[100010],cnte=-1;
void init(){
memset(first,-1,sizeof(first));
cnte=-1;clk=0;
memset(re,0,sizeof(re));memset(dfn,0,sizeof(dfn));
memset(back,0,sizeof(back));memset(top,0,sizeof(top));
memset(st,0,sizeof(st));
}
struct edge{
int to,next,w;
}a[200010];
inline void add(int u,int v,int w){
// cout<<"tree addedge "<<u<<' '<<v<<' '<<w<<'\n';
a[++cnte]=(edge){v,first[u],w};first[u]=cnte;
a[++cnte]=(edge){u,first[v],w};first[v]=cnte;
}
void dfs1(int u,int f){
int i,v;
dep[u]=dep[f]+1;fa[u]=f;st[u][0]=f;
siz[u]=1;son[u]=0;
for(i=first[u];~i;i=a[i].next){
v=a[i].to;if(v==f) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
}
void dfs2(int u,int t){
int i,v;
top[u]=t;
dfn[u]=++clk;back[clk]=u;
if(son[u]) dfs2(son[u],t);
for(i=first[u];~i;i=a[i].next){
v=a[i].to;if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
void ST(){
for(int j=1;j<=18;j++){
for(int i=1;i<=n;i++) st[i][j]=st[st[i][j-1]][j-1];
}
}
int getlca(int l,int r){
if(l==r) return l;
while(top[l]!=top[r]){
if(dep[top[l]]>=dep[top[r]]) swap(l,r);
r=fa[top[r]];
}
if(dep[l]>dep[r]) swap(l,r);
return l;
}
void solve(int l,int r,int val){
int lca=getlca(l,r),tmp,i;
if(dep[l]>dep[lca]+1){
tmp=l;
for(i=18;i>=0;i--) if(dep[st[tmp][i]]>dep[lca]+1) tmp=st[tmp][i];
while(top[l]!=top[tmp]){
seg::change(1,n,dfn[top[l]],dfn[l],1,val);
l=fa[top[l]];
}
seg::change(1,n,dfn[tmp],dfn[l],1,val);
}
if(dep[r]>dep[lca]+1){
tmp=r;
for(i=18;i>=0;i--) if(dep[st[tmp][i]]>dep[lca]+1) tmp=st[tmp][i];
while(top[r]!=top[lca]){
seg::change(1,n,dfn[top[r]],dfn[r],1,val);
r=fa[top[r]];
}
seg::change(1,n,dfn[tmp],dfn[r],1,val);
}
}
}
namespace ori{
int cnte=-1;
int id[100010],belong[100010];
struct edge{
int from,to,w,flag;
}a[100010];
vector<edge>e[100010];
void init(){
cnte=-1;
memset(id,0,sizeof(id));
memset(a,0,sizeof(a));
}
inline bool cmp(edge l,edge r){return l.w<r.w;}
inline void add(int u,int v,int w){
// cout<<"original add "<<u<<' '<<v<<' '<<w<<'\n';
a[++cnte]=(edge){u,v,w,0};
}
int kruskal(){
int i,u,v,ans=0,tmp,debug=0;
sort(a,a+cnte+1,cmp);
ufs::init(n);
for(i=0;i<=cnte;i++){
u=a[i].from;v=a[i].to;
if(tmp=ufs::join(u,v)){
a[i].flag=1;debug++;
t::add(u,v,a[i].w);
ans+=a[i].w;
if(tmp==n) break;
}
}
assert(debug==n-1);
return ans;
}
void solve(){
int i,tmp;
for(i=0;i<=cnte;i++){//处理非MST边
if(a[i].flag) continue;
tmp=t::getlca(a[i].from,a[i].to);
if(tmp!=a[i].from&&tmp!=a[i].to) e[tmp].push_back(a[i]);
t::solve(a[i].from,a[i].to,a[i].w);
}
}
inline int findb(int x){return ((belong[x]==x)?x:(belong[x]=findb(belong[x])));}//这里一定要再写一个集合合并
void getans(int u,int fromw){//一次dfs处理每个点的答案
int i,v,tot=0,x,y,cur=0,tmp=(n!=1),pre=e[u].size();
if(u!=1) id[fa[u]]=++tot;
for(i=t::first[u];~i;i=t::a[i].next){
v=t::a[i].to;if(v==fa[u]) continue;
getans(v,t::a[i].w);
id[v]=++tot;
fromw+=t::a[i].w;
if(u!=1&&seg::query(1,n,dfn[v],1)!=1e9) e[u].push_back((edge){id[fa[u]],id[v],seg::query(1,n,dfn[v],1),0});//找最小边
}
for(i=0;i<pre;i++){
e[u][i].from=id[findb(e[u][i].from)];
e[u][i].to=id[findb(e[u][i].to)];
}
sort(e[u].begin(),e[u].end(),cmp);
ufs::init(tot);
for(i=0;i<e[u].size();i++){
x=e[u][i].from;y=e[u][i].to;
assert(x<=tot);
assert(y<=tot);
if(tmp=ufs::join(x,y)){
cur+=e[u][i].w;
if(tmp==tot) break;
}
}
if(tot==1||tmp==tot) re[u]=base-fromw+cur;
else re[u]=1e9;
e[u].clear();
for(i=t::first[u];~i;i=t::a[i].next) if(t::a[i].to!=fa[u]) belong[t::a[i].to]=u;
}
} int main(){
int T=read(),i,j,t1,t2,t3,t4,tmp;
while(T--){
n=read();m=read();
ori::init();t::init();
for(i=1;i<=n;i++) ori::belong[i]=i;
for(i=1;i<=m;i++){
t1=read();t2=read();t3=read();t4=read();
ori::add(t1,t2,t3*(1-t4));
}
base=ori::kruskal();
t::dfs1(1,0);
t::dfs2(1,1);
t::ST();
seg::build(1,n,1);
ori::solve();
ori::getans(1,0);
for(i=1;i<=n;i++){
if(re[i]!=1e9) printf("%d\n",re[i]);
else puts("inf");
}
fflush(stdout);
}
}

[HDU3710] Battle Over Cities [树链剖分+线段树+并查集+kruskal+思维]的更多相关文章

  1. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  2. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  3. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  4. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

  5. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

  6. HDU4897 (树链剖分+线段树)

    Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...

  7. Aizu 2450 Do use segment tree 树链剖分+线段树

    Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...

  8. 【POJ3237】Tree(树链剖分+线段树)

    Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...

  9. HDU 2460 Network(双连通+树链剖分+线段树)

    HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链 ...

随机推荐

  1. 【ospf-链路验证】

    根据项目需求搭建好拓扑图 配置RT1的环回口IP和G0/0/0IP地址 开启RT1接口ospf认证,配置接口密码为H3C 配置RT1的ospf区域 同理 开启RT2接口ospf认证,配置接口密码为g0 ...

  2. 使用CSS3制作首页登录界面实例

    响应式设计 在这个页面中,使用下面3点来完成响应式设计 1.最大宽度 .设定了一个 max-width 的最大宽度,以便在大屏幕时兼容.: 2.margin : 30px auto; 使其保持时刻居中 ...

  3. Java学习笔记七:Java的流程控制语句之switch

    Java条件语句之 switch 当需要对选项进行等值判断时,使用 switch 语句更加简洁明了.例如:根据考试分数,给予前四名不同的奖品.第一名,奖励笔记本一台:第二名,奖励 IPAD 2 一个: ...

  4. 解决 Python2 和 Python3 的共存问题

    首先安装两种版本的Python 进入系统属性更改环境变量 将两个版本的安装路径找出. 添加至PATH中,变量之间用分号隔开. D:\Python36\Scripts\;D:\Python36\;D:\ ...

  5. 什么是OSS/BSS(电信业务)

    电信业务运营支持系统(BOSS),面对客户是统一的:面对电信运营商,它融合了业务支撑系统(BSS)与运营支撑系统(OSS),是一个综合的业务运营和管理平台,同时也是真正融合了传统IP数据业务与移动增值 ...

  6. Service ANR问题

    错误堆栈: ActivityManager: ANR in com.oppo.reader PID: 8071 Reason: executing service com.oppo.reade//co ...

  7. 云计算之路-阿里云上:受够了OCS,改用ECS+Couchbase跑缓存

    当今天早上在日志中发现这样的错误之后,对阿里云OCS(mecached缓存服务)的积怨倾泻而出. 2014-06-08 07:15:56,078 [ERROR] Enyim.Caching.Memca ...

  8. 关于transition动画下,如果有fixed元素,渲染的奇葩问题

    之前我们机票页面有生成一个低价日历,然后我们有一个需求就是滚动到那个月份,对应显示这个月,然后这个区域是fixed定位的,然后奇怪的事情发生了,就是低价日历的动画执行完后,修改页面的html却没有正常 ...

  9. Eclipse+APKTool动态调试APK

    1. 所需工具 Eclipse. Apktool v2.0.6. 安卓SDK工具. 2. 重编译APK apktool d -d -o test test.apk 此时当前test目录下就是apkto ...

  10. 「日常训练」 Finite or not? (CFR483D2C)

    题意(Codeforces 984C) 给定p,q,b" role="presentation">p,q,bp,q,b,问pq" role="p ...