题意

给出一个\(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. WPF设置ListBoxItem失去焦点时的背景色

    <!--全局ListBoxItem--> <Style TargetType="ListBoxItem"> <Style.Resources> ...

  2. 你真的认为iphone只是一部手机么

    闲言不表,直奔主题.我是一个程序员,上周参加了一个开源软件交流大会,其实会上并没有听到什么新鲜的东西.但是在会中,偶然间听到了一个关于iphone的秘密,却着实令我震惊了,事情具体是这样的,听我慢慢道 ...

  3. XAMPP设置tomcat自启动时,报无效的Win32程序

    最近给一个客户开发了一套系统,需要在内网中部署.系统是Java + Tomcat7 + mysql开发的. 考虑到客户内网不能上网的情况下,想使用XAMPP的便捷性,给客户进行部署.因为只需要Tomc ...

  4. Luogu1445 [Violet]樱花

    题面 题解 $$ \frac 1x + \frac 1y = \frac 1{n!} \\ \frac{x+y}{xy}=\frac 1{n!} \\ xy=n!(x+y) \\ xy-n!(x+y) ...

  5. 【MySQL安装】MySQL5.6在centos6.4上的安装

    卸载原来安装的mysql 安装从官网下载的mysql rpm包 发现有依赖,需要先安装libaio包和libnuma包 再装mysql就可以了 安装客户端 安装完成后,启动mysql 但是发现用没有m ...

  6. Docker--从安装到搭建环境

    docker 1. ubuntu下安装docker 安装docker有两种方法: 一种是用官方的bash脚本一键安装. 直接一条命令就解决了: $ curl -sSL https://get.dock ...

  7. NO--09今天遇到的一点小问题之axios全局注册

    今天用 Vue 写项目的时候,用到 axios ,因为 axios 不能用 Vue.use() ,所以在每个 .vue 文件中使用 axios 时就需要 import , .vue 文件少的话还好说, ...

  8. fastCMS数据库相关操作类

    fastCMS针对数据库的操作有以下几个类: 1.[paging_Class]分页类 此类用于分页检索数据库内符合条件的记录 1) 支持百万级数据分页 2) 支持多种类型的SQL语法,比如 Left ...

  9. 445. Cosine Similarity【LintCode java】

    Description Cosine similarity is a measure of similarity between two vectors of an inner product spa ...

  10. 使用calendar日历插件实现动态展示会议信息

    效果图如下,标红色为有会议安排,并跳转详细会议信息页面. html页面 <%@ page contentType="text/html;charset=UTF-8"%> ...