题意

给出一个\(n\)个点\(m\)条边的无向连通图,问删掉每一个点后的最小生成树权值和为多少(如果不存在最下生成树就输出inf)。

\(n\le 2\times 10^4,m\le 10^5\)

分析

换了个超级爽的青轴键盘后写题就开始加速了啊!这样非常不好!!!这道题在写的时候出了很多问题,以后还是要慢慢想清楚再写。

首先一个图的生成树(特别是最小生成树)这种问题,我们可以先把整体的最小生成树建出来。下面就是看删掉一个点会导致什么结果。

把生成树看成一颗有根树,那么删掉一个点会让它的子树,以及父亲节点互相分离。我们要把它用最小的代价连回去。这里的连边只有两种,子树之间的“横向边”和子树连到外面的“纵向边”。

子树内的边的处理基于一条\((x,y)\)边为“子树之间”的边,只有在它们的lca处才会出现一次这样的情况(如果在lca的上面那么它们就属于同一颗子树了)。这样我们就可以把这些边加到数组里。

注意到子树连到外面的多条边中,只有最小的那条是有用的,我们只要想办法求出一个子树连到外面的所有边中最小的就可以了。这可以用线段树合并(dfn序),可并堆(当一条边在一个子树中就pop),树链剖分(考虑在哪些点一条边为“纵向边”,显然是两点之间的路径上除了lca之外的所有点),这几种方法来实现。

这样我们就保证了总的求最小生成树的边数为\(O(n+m)\),所以可以在\(O(n+m\log (n+m))\)的时间内解决这个问题。

这里的子树之间的情况分析在别的题中也有广泛的应用。

代码

#include<cstdio>
#include<cctype>
#include<cstring>
#include<vector>
#include<utility>
#include<algorithm>
#define M(x) memset(x,0,sizeof x)
using namespace std;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int maxn=2e4+1;
const int maxm=1e5+1;
const int nlogn=3e6;
const int maxj=15;
const int inf=1e9+7;
typedef pair<int,int> Pair;
Pair operator + (Pair a,Pair b) {
return min(a,b);
}
struct bian {
int x,y,w;
bool alr;
bian (int x=0,int y=0,int w=0):alr(false),x(x),y(y),w(w) {}
inline bool operator < (const bian &a) const {return w<a.w;}
} b[maxm];
int with[maxn],ans[maxn],n,m,base,root[maxn];
namespace uns {
int f[maxn];
void clear(int n) {for (int i=1;i<=n;++i) f[i]=i;}
int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
}
namespace sgt {
struct node {
int l,r;
Pair dat;
} t[nlogn];
int tot;
void update(int x) {
if (t[x].l) t[x].dat=t[x].dat+t[t[x].l].dat;
if (t[x].r) t[x].dat=t[x].dat+t[t[x].r].dat;
}
int merge(int x,int y,int l,int r) {
if (!x) return y;
if (!y) return x;
if (l==r) {
t[x].dat=t[x].dat+t[y].dat;
return x;
}
int mid=(l+r)>>1;
t[x].l=merge(t[x].l,t[y].l,l,mid);
t[x].r=merge(t[x].r,t[y].r,mid+1,r);
update(x);
return x;
}
Pair query(int x,int L,int R,int l,int r) {
if (!x) return make_pair(inf,0);
if (L==l && R==r) return t[x].dat;
int mid=(L+R)>>1;
if (r<=mid) return query(t[x].l,L,mid,l,r);
if (l>mid) return query(t[x].r,mid+1,R,l,r);
return query(t[x].l,L,mid,l,mid)+query(t[x].r,mid+1,R,mid+1,r);
}
Pair query(int x,int l,int r) {
if (l>r) return make_pair(inf,0);
return query(x,1,n,l,r);
}
void modify(int &x,int l,int r,int p,Pair d) {
if (!x) t[x=++tot]=(node){0,0,make_pair(inf,0)};
if (l==r) {
t[x].dat=t[x].dat+d;
return;
}
int mid=(l+r)>>1;
p<=mid?modify(t[x].l,l,mid,p,d):modify(t[x].r,mid+1,r,p,d);
update(x);
}
void modify(int &x,int p,Pair d) {
modify(x,1,n,p,d);
}
}
namespace tree {
vector<int> g[maxn];
vector<bian> on[maxn];
int first[maxn],second[maxn],dfx,f[maxn][maxj],dep[maxn];
int id[maxn];
int getid(int x) {return id[x]==x?x:id[x]=getid(id[x]);}
void clear(int n) {
M(first),M(second),M(f),M(dep),dfx=0;
for (int i=1;i<=n;++i) id[i]=i;
for (int i=1;i<=n;++i) g[i].clear(),on[i].clear();
}
void add(int x,int y) {g[x].push_back(y);}
void dfs(int x,int fa) {
f[x][0]=fa;
dep[x]=dep[fa]+1;
first[x]=++dfx;
for (int v:g[x]) if (v!=fa) dfs(v,x);
second[x]=dfx;
}
void run() {
for (int j=1;j<maxj;++j) for (int i=1;i<=n;++i) f[i][j]=f[f[i][j-1]][j-1];
}
int lca(int x,int y) {
if (dep[x]<dep[y]) swap(x,y);
for (int j=maxj-1;j>=0;--j) if (dep[f[x][j]]>=dep[y]) x=f[x][j];
if (x==y) return x;
for (int j=maxj-1;j>=0;--j) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
return f[x][0];
}
void work(int x,int fa) {
vector<bian> &e=on[x];
int gs=(x!=1);
for (int v:g[x]) if (v!=fa) {
work(v,x);
++gs;
uns::f[v]=v;
if (x==1) continue;
Pair ret1=sgt::query(root[v],1,first[x]-1);
Pair ret2=sgt::query(root[v],second[x]+1,n);
Pair ret=ret1+ret2;
if (ret.first<inf) e.push_back((bian){x,v,ret.first});
root[x]=sgt::merge(root[x],root[v],1,n);
}
uns::f[x]=x;
sort(e.begin(),e.end());
int &tmp=ans[x]=base-with[x],j=0;
for (int i=0;i<e.size() && j<gs-1;++i) {
int u=getid(e[i].x),v=getid(e[i].y),w=e[i].w;
int fx=uns::find(u),fy=uns::find(v);
if (fx!=fy) {
uns::f[fx]=fy;
++j;
tmp+=w;
}
}
if (j<gs-1) tmp=-1;
for (int v:g[x]) if (v!=fa) id[v]=x;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
int T=read();
while (T--) {
n=read(),m=read();
base=0;
M(root),M(with);
tree::clear(n);
uns::clear(n);
for (int i=1;i<=m;++i) {
int x=read(),y=read(),d=read(),c=read(),w=d*(1-c);
b[i]=(bian){x,y,w};
}
sort(b+1,b+m+1);
for (int i=1,j=0;j<n-1 && i<=m;++i) {
int x=b[i].x,y=b[i].y,w=b[i].w;
int fx=uns::find(x),fy=uns::find(y);
if (fx!=fy) {
++j;
tree::add(x,y),tree::add(y,x);
uns::f[fx]=fy;
with[x]+=w,with[y]+=w;
b[i].alr=true;
base+=w;
}
}
tree::dfs(1,1);
tree::run();
sgt::tot=0;
for (int i=1;i<=m;++i) if (!b[i].alr) {
int x=b[i].x,y=b[i].y,w=b[i].w;
if (tree::dep[x]>tree::dep[y]) swap(x,y);
int l=tree::lca(x,y);
if (x!=l) tree::on[l].push_back(b[i]);
sgt::modify(root[y],tree::first[x],make_pair(w,y));
sgt::modify(root[x],tree::first[y],make_pair(w,x));
}
tree::work(1,1);
for (int i=1;i<=n;++i) ans[i]==-1?puts("inf"):printf("%d\n",ans[i]);
}
return 0;
}

HDU3710-Battle Over Cities的更多相关文章

  1. HDU3710 Battle over Cities(最小生成树+树链剖分+倍增+线段树)

    Battle over Cities Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Othe ...

  2. [HDU3710] Battle Over Cities [树链剖分+线段树+并查集+kruskal+思维]

    题面 一句话题意: 给定一张 N 个点, M 条边的无向连通图, 每条边上有边权 w . 求删去任意一个点后的最小生成树的边权之和. 思路 首先肯定要$kruskal$一下 考虑$MST$里面去掉一个 ...

  3. PAT 解题报告 1013. Battle Over Cities (25)

    1013. Battle Over Cities (25) t is vitally important to have all the cities connected by highways in ...

  4. PAT1013: Battle Over Cities

    1013. Battle Over Cities (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue It ...

  5. PAT-Top1001. Battle Over Cities - Hard Version (35)

    在敌人占领之前由城市和公路构成的图是连通图.在敌人占领某个城市之后所有通往这个城市的公路就会被破坏,接下来可能需要修复一些其他被毁坏的公路使得剩下的城市能够互通.修复的代价越大,意味着这个城市越重要. ...

  6. PAT 1013 Battle Over Cities

    1013 Battle Over Cities (25 分)   It is vitally important to have all the cities connected by highway ...

  7. PAT Battle Over Cities [未作]

    1013 Battle Over Cities (25)(25 分) It is vitally important to have all the cities connected by highw ...

  8. PTA (Advanced Level) 1013 Battle Over Cities

    Battle Over Cities It is vitally important to have all the cities connected by highways in a war. If ...

  9. PAT甲级1013. Battle Over Cities

    PAT甲级1013. Battle Over Cities 题意: 将所有城市连接起来的公路在战争中是非常重要的.如果一个城市被敌人占领,所有从这个城市的高速公路都是关闭的.我们必须立即知道,如果我们 ...

  10. PAT 1013 Battle Over Cities(并查集)

    1013. Battle Over Cities (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue It ...

随机推荐

  1. 20155336虎光元实验四 Android开发基础

    20155336虎光元实验四 Android开发基础 实验内容 1:完成Hello World, 要求修改res目录中的内容,Hello World后要显示自己的学号 2:创建 ThirdActivi ...

  2. 20145226夏艺华 Exp6 信息搜集与漏洞扫描

    20145226夏艺华 Exp6 信息搜集与漏洞扫描 基础问题回答 哪些组织负责DNS,IP的管理? · 全球根服务器均由美国政府授权的ICANN统一管理,负责全球的域名根服务器.DNS和IP地址管理 ...

  3. SaltStack入门篇(一)之SaltStack部署

    一.SaltStack概述 Salt,,一种全新的基础设施管理方式,部署轻松,在几分钟内可运行起来,扩展性好,很容易管理上万台服务器,速度够快,服务器之间秒级通讯. salt底层采用动态的连接总线, ...

  4. STM32L431仿真卡在HAL_InitTick(TICK_INT_PRIORITY);

    1. 使用IAR 8.20版本,STM32L431RBT芯片,JLINK V9仿真器,实际仿真测试的时候卡在如下的函数 /* Use SysTick as time base source and c ...

  5. MindMaster安装教程以及激活破解教程

    原文地址:https://www.jianshu.com/p/16d2fc7d8e45 第一.激活必须首先断网 第二.运行安装程序,安装完成后先不要打开 第三.把Cracks文件夹下的文件复制到软件安 ...

  6. shell loop

    #!/bin/sh date i=0 while [ $i -le 30 ] do         echi $i /usr/sbin/r2/np_test_acl -f rule.txt i=$(e ...

  7. bootstrap form样式及数据提交

    1.基本form布局 想要把form表单弄成两列的表格样式,奈何前端不太懂,记录下样式便于下次使用. form-group :增加盒子的下边界 form-control: 充满整个父元素,并且有换行作 ...

  8. Python+selenium+pil+tesseract实现自动识别验证码

    一.环境搭建准备: 1.Python下载,安装以及环境配置 2.IDE pycharm 工具下载,安装 3.ie浏览器 4.selenium 5.pil:pil第三方库的下载,win下安装whl文件, ...

  9. #Ubuntu 18.04 安装tensorflow-gpu 1.9

    参考 https://tensorflow.google.cn/install/install_linux http://nvidia.com/cuda http://developer.nvidia ...

  10. JQuery点击打开再点击关闭

    $("#03").click(function() { $("#03").show(speed); $("#03").css("c ...