题面

一句话题意:

给定一张 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. 解决每次运行Xcode,都需要输入密码的问题

    新买的Mac,在安装了 Xcode 7.1的时候,不知道是配置信息哪里手残了一下,导致每次运行Xcode模拟器 后 都需要输入一次密码. 为此在网上也是查阅了不少的资料,当时 所谓的 XCode--- ...

  2. 【super vlan的配置】

    Super vlan的配置 一:根据项目需求搭建好拓扑图如下: 二:配置 1:由项目图进行理论分析:sw1属于三层交换,用于二层交换信息的转发;同时在sw1上可定义super vlan把sub vla ...

  3. css3新样式

    超出两行变省略号 overflow:hidden; text-overflow:ellipsis;display:-webkit-box; -webkit-box-orient:vertical;-w ...

  4. PHP令人困惑的strtotime

    经常会有人被strtotime结合-1 month, +1 month, next month的时候搞得很困惑, 然后就会觉得这个函数有点不那么靠谱, 动不动就出问题. 用的时候就会很慌… 比如:今天 ...

  5. IT类职位常用缩写 SA SD RD PG PM DBA MIS QA Sales

    身为IT民工的基本常识,IT类职位常用缩写 SA (System Analyst) 系统分析师 在软体开发团队中,属于中高阶的基层管理者与领导者.除了须具备优秀的文字.语言沟通能力之外,还要有良好的分 ...

  6. UVA11988 Broken Keyboard (a.k.a. Beiju Text)【数组模拟链表】

    参考:https://blog.csdn.net/lianai911/article/details/41831645 #include <iostream> #include <c ...

  7. 12、K最近邻算法(KNN算法)

    一.如何创建推荐系统? 找到与用户相似的其他用户,然后把其他用户喜欢的东西推荐给用户.这就是K最近邻算法的分类作用. 二.抽取特征 推荐系统最重要的工作是:将用户的特征抽取出来并转化为度量的数字,然后 ...

  8. 【Consul】Consul架构-Consensus协议

    Consul使用Consensus协议提供一致性(Consistency)--CAP定义的一致性.Consensus协议是基于"Raft:In search of an Understand ...

  9. Fiddler 发送post 请求失败

    今天服务端同事,让我发一个post 请求.然后呢,一直有问题.告诉我签名失败. 后来换了其他的在线模拟post,都是可以的. 后来找到原因了, post 请求,必须要有Content-Type 和 C ...

  10. 【数据库】 SQL 常用语句

    [数据库] SQL 常用语句 1.批量导入 INSERT INTO Table2(field1,field2,...) SELECT value1,value2,... FROMTable1 要求目标 ...