[HDU3710] Battle Over Cities [树链剖分+线段树+并查集+kruskal+思维]
题面
一句话题意:
给定一张 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+思维]的更多相关文章
- 【BZOJ-2325】道馆之战 树链剖分 + 线段树
2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 1153 Solved: 421[Submit][Statu ...
- 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树
[BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...
- BZOJ2243 (树链剖分+线段树)
Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...
- POJ3237 (树链剖分+线段树)
Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...
- bzoj4034 (树链剖分+线段树)
Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...
- HDU4897 (树链剖分+线段树)
Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...
- 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 ...
- 【POJ3237】Tree(树链剖分+线段树)
Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...
- HDU 2460 Network(双连通+树链剖分+线段树)
HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链 ...
随机推荐
- 【cisco下针对冗余链路故障备份的处理措施】
对于中小型的网络中,为了流量的分担,可制定负载均衡方案,但往往带来的是链路的冗余.导致多条物理线路不能够最大的发挥其作用:冗余链路随可避免环路,但在实际的网络中还是存在一些需要完善的地方: 假设有一组 ...
- 【ssh服务配置】
根据项目需求,搭建好拓扑图如下: 第一种验证方式:给予密码和用户名登录 Ssh server配置: 首先在服务器上创建一个rsa加密算法的秘钥对: 对ssh服务进行开启: 创建用户的虚拟终端登录界面: ...
- php后端跨域Header头
header("Access-Control-Allow-Origin: http://a.com"); // 允许a.com发起的跨域请求 //如果需要设置允许所有域名发起的跨域 ...
- MongoDB学习(1)--安装,基本curd操作
知识点: 1-MongoDB 安装,启动和卸载 2-基本概念 3-基本的增删改查操作(CURD) 来回顾总结一把学习的mongodb,如果有javascript基础,学习"芒果DB" ...
- while,格式化输出
1. while循环: while 条件: 代码块(循环体) num=1 while num<=5: print(num) num+=1 break:结束循环;停止当前本层循环 continue ...
- 网络编程之socket的运用
一,socket用法 socket是什么 ? Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐 ...
- 链栈的c++实现
2013-08-30 20:58 1876人阅读 评论(0) 收藏 举报 链栈是借用单链表实现的栈.其不同于顺序栈之处在于: 1.链栈的空间是程序运行期间根据需要动态分配的,机器内存是它的上限.而顺序 ...
- 【Java】Spring MVC 扩展和SSM框架整合
开发web项目通常很多地方需要使用ajax请求来完成相应的功能,比如表单交互或者是复杂的UI设计中数据的传递等等.对于返回结果,我们一般使用JSON对象来表示,那么Spring MVC中如何处理JSO ...
- mybatis-generator自定义注释生成
最近做的项目发现没有中文注释,故查找资料,特此记录. 本文所用的是基于mybatis-generator 1.3.2版本来完成的. mybatis-generator 自动生成的代码注释是很反人类的, ...
- OpenCV代码提取: threshold函数的实现
threshold algorithm: The simplest image segmentation method. All thresholding algorithms take a sour ...