题意

给出一个\(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. 资源很多,你却不会使用——以不变应万变才是自学Java的正确方法

    鄙人乐于寻找学习方法,在这里提出自己的见解,希望可以帮助想玩好Java而又感觉很难上手的同学对Java不再恐惧 现状 我们的同学们除了某月,某婷等等大神以外,想必仍然存在着一大批同学根本没有摸索到学习 ...

  2. 20155339 2016-2017-2 《Java程序设计》第十周学习总结

    20155339 2016-2017-2 <Java程序设计>第十周学习总结 教材学习内容总结 计算机网络概述 在计算机网络中,现在命名IP地址的规定是IPv4协议,该协议规定每个IP地址 ...

  3. wpf- DataGrid 常用属性和事件

    组件所在命名空间: System.Windows.Controls 组件常用方法: BeginEdit:使DataGrid进入编辑状态. CancelEdit:取消DataGrid的编辑状态. Col ...

  4. 【LG2183】[国家集训队]礼物

    [LG2183][国家集训队]礼物 题面 洛谷 题解 插曲:不知道为什么,一看到这个题目,我就想到了这个人... 如果不是有\(exLucas\),这题就是\(sb\)题... 首先,若\(\sum_ ...

  5. 1722: [Usaco2006 Mar] Milk Team Select 产奶比赛

    1722: [Usaco2006 Mar] Milk Team Select 产奶比赛 https://www.lydsy.com/JudgeOnline/problem.php?id=1722 分析 ...

  6. Cannot get connection for URL jdbc:oracle:thin:调用中无效参数

    这个报错明显是连接数据库的url没有写对,但是,我要说的是但是,同样的代码生产没有问题,而测试环境报错了.最终哥找到那个错误,jdbc连接数据库时,有ResultSet,PreparedStateme ...

  7. [转]操作系统Unix、Windows、Mac OS、Linux的故事

    [写得很江湖气,可惜找不到原作者了] 文章转自:http://blog.csdn.net/wenmingchan/article/details/49925379 http://www.jb51.ne ...

  8. Django——test文件编写接口测试

    用自己建立的小网页来做接口测试,在Django的tests.py写下如下 test_login_page为用get方式登录login路径,根据回复验证是否查看到页面 test_login_action ...

  9. 《物质世界 (Outward)》证明不必压缩制作大型角色扮演游戏的时间

    <物质世界>是一款雄心勃勃的开放世界角色扮演游戏 (RPG),设计这款游戏的公司规模只有您预期的三分之一.游戏中的一切都是动态的,拥有许多炫酷的系统设计,大家可以分屏合作掌控整个场景.参与 ...

  10. oracle数据库之组函数

    组函数也叫聚合函数,用来对一组值进行运算,并且可以返回单个值 常见的组函数: (1)count(*),count(列名)  统计行数:找到所有不为 null 的数据来统计行数 (2)avg(列名)  ...