题意

给出一个\(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. CH03 课下作业

    CH03 课下作业 缓冲区溢出漏洞实验 缓冲区溢出攻击:通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,造成程序崩溃或使程序转而执行其它指令,以达到攻击的目的. 缓冲区溢出 ...

  2. Kali linux更新源

    1.更新软件源: 修改sources.list文件: leafpad /etc/apt/sources.list 然后选择添加以下适合自己较快的源(可自由选择,不一定要全部): #官方源deb htt ...

  3. dedecms 后台网站 标题设置

    打开文件夹,找到dede/templets/index2.htm,修改第6行就行了

  4. WPF MVVM从入门到精通3:数据绑定

    原文:WPF MVVM从入门到精通3:数据绑定   WPF MVVM从入门到精通1:MVVM模式简介 WPF MVVM从入门到精通2:实现一个登录窗口 WPF MVVM从入门到精通3:数据绑定 WPF ...

  5. HTML 5 +CSS3 + 原生js 做(雪花全屏飘落 + 3d旋转图)

    原文:HTML 5 +CSS3 + 原生js 做(雪花全屏飘落 + 3d旋转图) 3d旋转图:主要用css3中transform属性中的rotate,translate;以及用来做舞台效果的 pers ...

  6. 【AHOI2013】差异

    题面 题解 $ \because \sum_{1 \leq i < j \leq n} i + j = \frac{n(n-1)(n+1)}2 $ 所以只需求$\sum lcp(i,j)$即可. ...

  7. xpath基础

    XML:一种可扩展标记语言,HTML就是一种XML XPATH:也是一个W3C标准,在所有XML中均可使用 XPATH的路径规则 /表示跟节点 /html 表示html这个元素 /html/body ...

  8. 【JUC源码解析】LinkedBlockingQueue

    简介 一个基于链表的阻塞队列,FIFO的顺序,head指向的元素等待时间最长,tail指向的元素等待时间最短,新元素从队列尾部添加,检索元素从队列头部开始,队列的容量,默认是Integer#MAX_V ...

  9. 建表/修改表名/增加删除字段(MySql)

    修改表名:alter table 旧表名 rename 新表名; 删除字段:alter table 表名 drop 字段名; 增加字段:alter table 表名 add 字段名 字段类型 [def ...

  10. 基于OpenSSL的RSA加密应用(非算法)

    基于OpenSSL的RSA加密应用(非算法) iOS开发中的小伙伴应该是经常用der和p12进行加密解密,而且在通常加密不止一种加密算法,还可以加点儿盐吧~本文章主要阐述的是在iOS中基于openSL ...