网络战争 [KD-Tree+最小割树]

题面

思路

首先吐槽一下:

这题是什么东西啊??出题人啊,故意拼题很有意思吗??还拼两个这么毒瘤的东西????

10K代码了解一下????

然后是正经东西

首先,本题可以理解为这样:

给定$n$个块,每个块有一个根,每个根只会主动连出去一条无向边,每次求两点最小割

那么,我们显然可以把每个块内的最小割树建立出来,同时把块的根之间的最小割树也建立出来

如果询问点在同一个块里面,显然可以直接最小割树处理

否则就是两边的点到块根的最小割和两个块根之间的最小割的最小值

所以,我们先对于所有的块根,建出KDTree,然后求得最近点对连边

这里有一个性质:快根之间不会有除了重边之外的环,也就是说不考虑重边的话,块根之间连成一个森林

这样我们直接在块根森林上倍增就好了

求最小割树的方法可以参考ZJOI2011

但是此处需要这样操作:

每次选定的$S$和$T$之间连一条边,边权是这一次求出的$S-T$最小割

两点之间的最小割就是两点路径上的最小边权了

所以我们要维护$KD-Tree$,最小割树,块根的森林,还有后面两个东西上的倍增求最小值

呵呵呵呵呵呵呵呵呵呵

Code

/**************************************************************
????Problem: 1532
????User: szlhx01
****************************************************************/
?
?
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cassert>
#include<vector>
#define mp make_pair
#define rank DEEP_DARK_FANTASY
#define next VAN_YOU_SEE
using namespace std;
inline int read(){
????int re=0,flag=1;char ch=getchar();
????while(ch>'9'||ch<'0'){
????????if(ch=='-') flag=-1;
????????ch=getchar();
????}
????while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
????return re*flag;
}
//KD-TREE
struct node{
????int ll,lr,rl,rr,num,posl,posr,lc,rc;
}x[200010],kdtr[200010];int L;
inline bool operator <(const node p,const node o){
????if(L) return p.posl<o.posl;
????else return p.posr<o.posr;
}
void update(int pos){
????kdtr[pos].ll=kdtr[pos].lr=kdtr[pos].posl;
????if(kdtr[pos].lc) kdtr[pos].ll=min(kdtr[pos].ll,kdtr[kdtr[pos].lc].ll);
????if(kdtr[pos].rc) kdtr[pos].ll=min(kdtr[pos].ll,kdtr[kdtr[pos].rc].ll);
????if(kdtr[pos].lc) kdtr[pos].lr=max(kdtr[pos].lr,kdtr[kdtr[pos].lc].lr);
????if(kdtr[pos].rc) kdtr[pos].lr=max(kdtr[pos].lr,kdtr[kdtr[pos].rc].lr);
????kdtr[pos].rl=kdtr[pos].rr=kdtr[pos].posr;
????if(kdtr[pos].lc) kdtr[pos].rl=min(kdtr[pos].rl,kdtr[kdtr[pos].lc].rl);
????if(kdtr[pos].rc) kdtr[pos].rl=min(kdtr[pos].rl,kdtr[kdtr[pos].rc].rl);
????if(kdtr[pos].lc) kdtr[pos].rr=max(kdtr[pos].rr,kdtr[kdtr[pos].lc].rr);
????if(kdtr[pos].rc) kdtr[pos].rr=max(kdtr[pos].rr,kdtr[kdtr[pos].rc].rr);
}
int build(int l,int r,int now){
????int mid=(l+r)>>1;L=now;
????nth_element(x+l,x+mid,x+r+1);
????kdtr[mid]=x[mid];
????if(mid>l) kdtr[mid].lc=build(l,mid-1,now^1);
????if(mid<r) kdtr[mid].rc=build(mid+1,r,now^1);
????update(mid);
????return mid;
}
int getd(int XX,int YY,int xx,int yy){
????return (XX-xx)*(XX-xx)+(YY-yy)*(YY-yy);
}
int dis(int p,int X,int Y){
????int xx=0,yy=0;
????if(kdtr[p].lr<X) xx=X-kdtr[p].lr;
????if(kdtr[p].ll>X) xx=kdtr[p].ll-X;
????if(kdtr[p].rr<Y) yy=Y-kdtr[p].rr;
????if(kdtr[p].rl>Y) yy=kdtr[p].rl-Y;
????return xx*xx+yy*yy;
}
int ans,pp,curnum;
void query(int X,int Y,int pos){
????int dl=1e9,dr=1e9,d=getd(X,Y,kdtr[pos].posl,kdtr[pos].posr);
????if((kdtr[pos].num!=curnum)&&(ans>d||(ans==d&&pp>kdtr[pos].num))) ans=d,pp=kdtr[pos].num;
????if(kdtr[pos].lc) dl=dis(kdtr[pos].lc,X,Y);
????if(kdtr[pos].rc) dr=dis(kdtr[pos].rc,X,Y);
????if(dl<dr){
????????if(dl<=ans) query(X,Y,kdtr[pos].lc);
????????if(dr<=ans) query(X,Y,kdtr[pos].rc);
????}
????else{
????????if(dr<=ans) query(X,Y,kdtr[pos].rc);
????????if(dl<=ans) query(X,Y,kdtr[pos].lc);
????}
}

int tot,cap[50010];
//维护最小割树(森林)的倍增
namespace orig{
????vector<pair<int,int> >e[100010];
????int dep[100010],st[100010][20],minn[100010][20],dis[100010];
????void dfs(int u,int f,int w){
????????dep[u]=dep[f]+1;st[u][0]=f;minn[u][0]=w;
????????if(f!=0) dis[u]=min(dis[f],w);
????????else dis[u]=1e9;//这里dis是预处理了最小割树中每个点到1(块根)的路径上最小边权,方便查询
????????assert(dis[u]>0);
????????for(auto v:e[u]){
????????????if(v.first==f) continue;
????????????dfs(v.first,u,v.second);
????????}
????}
????void ST(){
????????int i,j;
????????for(j=1;j<=17;j++)
????????????for(i=1;i<=tot;i++)
????????????????st[i][j]=st[st[i][j-1]][j-1];
????????for(j=1;j<=17;j++)
????????????for(i=1;i<=tot;i++){
????????????????if(dep[i]>(1<<j))
????????????????????minn[i][j]=min(minn[i][j-1],minn[st[i][j-1]][j-1]);
????????????}
????}
????int query(int l,int r){
????????int i,re=1e9;
????????if(dep[l]>dep[r]) swap(l,r);
????????assert(dep[0]==0);
????????for(i=17;i>=0;i--){
????????????if(dep[st[r][i]]>=dep[l]){
????????????????re=min(re,minn[r][i]);
????????????????r=st[r][i];
????????????}
????????}
????????if(l==r) return re;
????????for(i=17;i>=0;i--){
????????????if(st[l][i]!=st[r][i]){
????????????????re=min(re,min(minn[l][i],minn[r][i]));
????????????????r=st[r][i];l=st[l][i];
????????????}
????????}
????????assert(re>0);
????????return min(re,min(minn[r][0],minn[l][0]));
????}
}

//维护块根森林的倍增
namespace bigg
????vector<pair<int,int> >e[100010];
????int dep[100010],st[100010][20],minn[100010][20],n;
????int numcnt=0,belong[100010];
????void dfs(int u,int f,int w){
????????dep[u]=dep[f]+1;st[u][0]=f;minn[u][0]=w;
????????belong[u]=numcnt;
????????for(auto v:e[u]){
????????????if(v.first==f) continue;
????????????dfs(v.first,u,v.second);
????????}
????}
????void ST(){
????????int i,j;
????????for(j=1;j<=17;j++)
????????????for(i=1;i<=n;i++)
????????????????st[i][j]=st[st[i][j-1]][j-1];
????????for(j=1;j<=17;j++)
????????????for(i=1;i<=n;i++){
????????????????if(dep[i]>(1<<j))
????????????????????minn[i][j]=min(minn[i][j-1],minn[st[i][j-1]][j-1]);
????????????}
????}
????int query(int l,int r){
????????int i,re=1e9;
????????if(belong[l]!=belong[r]){return 0;}
????????if(dep[l]>dep[r]) swap(l,r);
????????for(i=17;i>=0;i--){
????????????if(dep[st[r][i]]>=dep[l]){
????????????????re=min(re,minn[r][i]);
????????????????r=st[r][i];
????????????}
????????}
????????if(l==r) return re;
????????for(i=17;i>=0;i--){
????????????if(st[l][i]!=st[r][i]){
????????????????re=min(re,min(minn[l][i],minn[r][i]));
????????????????r=st[r][i];l=st[l][i];
????????????}
????????}
????????assert(re>0);
????????return min(re,min(minn[r][0],minn[l][0]));
????}
}

//dinic网络流+最小割树
namespace NETW{
????int n=0,first[50010],cnte,dep[50010],cur[50010];
????bool vis[50010];int node[50010];
????struct edge{
????????int to,next,w,ori;
????}a[120010];
????inline void add(int u,int v,int w){
????????a[++cnte]=(edge){v,first[u],w,w};first[u]=cnte;
????????a[++cnte]=(edge){u,first[v],w,w};first[v]=cnte;
????}
????void renew(int num){
????????n=num;
????????for(int i=1;i<=n;i++) node[i]=i;
????}
????void clear(){
????????int i;
????????for(i=1;i<=n;i++) first[i]=-1;
????????cnte=-1;
????}
????int q[50010],head=0,tail;
????bool bfs(int s,int t){
????????int i,u,v;head=0;tail=1;
????????for(i=1;i<=n;i++) dep[i]=-1,cur[i]=first[i];
????????q[0]=s;dep[s]=0;
????????while(head<tail){
????????????u=q[head++];
????????????for(i=first[u];~i;i=a[i].next){
????????????????v=a[i].to;
????????????????if(~dep[v]||!a[i].w) continue;
????????????????dep[v]=dep[u]+1;
????????????????q[tail++]=v;
????????????}
????????}
????????return ~dep[t];
????}
????int dfs(int u,int t,int lem){
????????if(u==t||!lem) return lem;
????????int i,v,f,flow=0;
????????for(i=first[u];~i;i=a[i].next){
????????????v=a[i].to;
????????????if(dep[v]==dep[u]+1&&(f=dfs(v,t,min(lem,a[i].w)))){
????????????????a[i].w-=f;a[i^1].w+=f;
????????????????flow+=f;lem-=f;
????????????????if(!lem) return flow;
????????????}
????????}
????????return flow;
????}
????int dinic(int s,int t){//dinic
????????int re=0;
????????while(bfs(s,t)) re+=dfs(s,t,1e9);
????????return re;
????}
????int bl[50010],br[50010],tl,tr;
????void cut(int u){
????????int i,v;vis[u]=1;
????????for(i=first[u];~i;i=a[i].next){
????????????v=a[i].to;
????????????if(a[i].w&&!vis[v]) cut(v);
????????}
????}
????void solve(int l,int r){//最小割树
????????int i,tmp,ttl,S=node[l],T=node[r];
????????if(l==r) return;
????????tl=tr=0;
????????for(i=0;i<=cnte;i++) a[i].w=a[i].ori;
????????tmp=dinic(S,T);
?
????????memset(vis,0,sizeof(vis));
????????cut(S);
?????????
????????for(i=l;i<=r;i++){
????????????if(!vis[node[i]]) bl[++tl]=node[i];
????????????else br[++tr]=node[i];
????????}
?
????????for(i=1;i<=tl;i++) node[l+i-1]=bl[i];
????????for(i=1;i<=tr;i++) node[l+tl-1+i]=br[i];
????????ttl=tl;
????????solve(l,l+ttl-1);
????????solve(l+ttl,r);
????????orig::e[tot+S].push_back(mp(tot+T,tmp));
????????orig::e[tot+T].push_back(mp(tot+S,tmp));
????????return ;
????}
}
int mate[50010];
int n,px[50010],py[50010],c[50010],m[50010];
int main(){
????n=read();int i,j,t1,t2,t3,t4;bigg::n=n;
????memset(orig::minn,127,sizeof(orig::minn));
????memset(bigg::minn,127,sizeof(bigg::minn));
?
????for(i=1;i<=n;i++){
?
????????px[i]=read();
????????py[i]=read();
????????c[i]=read();
?
????????x[i].posl=px[i];
????????x[i].posr=py[i];
????????x[i].num=i;
?
????????t4=read();
????????m[i]=read();
????????cap[i]=tot;
????????NETW::renew(t4);NETW::clear();
????????for(j=1;j<=m[i];j++){
????????????t1=read();t2=read();t3=read();
????????????NETW::add(t1,t2,t3);
????????}
????????NETW::solve(1,t4);
????????orig::dfs(tot+1,0,0);
????????tot+=t4;
????}
?
????int root=build(1,n,1);
????NETW::renew(n);NETW::clear();
????for(i=1;i<=n;i++){
????????ans=pp=1e9;curnum=i;
????????query(px[i],py[i],root);
????????mate[i]=pp;
????}
????for(i=1;i<=n;i++){
????????if(mate[i]==-1) continue;
????????if(mate[mate[i]]==i){
????????????bigg::e[i].push_back(mp(mate[i],c[i]+c[mate[i]]));
????????????bigg::e[mate[i]].push_back(mp(i,c[i]+c[mate[i]]));
????????????mate[mate[i]]=-1;
????????}
????????else{
????????????bigg::e[i].push_back(mp(mate[i],c[i]));
????????????bigg::e[mate[i]].push_back(mp(i,c[i]));
????????}
????}
?
????orig::ST();
?????
????for(i=1;i<=n;i++) if(!bigg::dep[i]) bigg::numcnt++,bigg::dfs(i,0,0);
????bigg::ST();
?
????int Q=read();
????while(Q--){
????????t1=read();t2=read();t3=read();t4=read();
????????if(t1==t2) printf("%d\n",orig::query(cap[t1]+t3,cap[t1]+t4));
????????else printf("%d\n",min(bigg::query(t1,t2),min(orig::dis[cap[t1]+t3],orig::dis[cap[t2]+t4])));
????}
}

原文地址:https://www.cnblogs.com/dedicatus545/p/9690255.html

时间: 2024-11-08 12:03:55

网络战争 [KD-Tree+最小割树]的相关文章

[2016北京集训试题6]网络战争-[最小割树(网络流)+kd-tree+倍增]

Description A 联邦国有 N 个州,每个州内部都有一个网络系统,有若干条网络线路,连接各个 州内部的城市. 由于 A 国的州与州之间的关系不是太好,每个州都只有首府建立了到别的州的网络.具体来说,每个州的首府都只主动地建立了一条网络线路,连接到距离最近的州的 首府.(欧氏距离.如果有多个,选择标号最小的去连接) B 国探知了 A 国的网络线路分布情况,以及攻陷每条网络线路所需花费的代价,B 国首脑想知道断开 A 国某两个城市之间的网络连接,所需的最少代价.请你计算出来告 诉他. 注:

【模板】最小割树(Gomory-Hu Tree)

传送门 Description 给定一个\(n\)个点\(m\)条边的无向连通图,多次询问两点之间的最小割 两点间的最小割是这样定义的:原图的每条边有一个割断它的代价,你需要用最小的代价使得这两个点不连通 Solution 对于一张无向图,如果 \(s \rightarrow t\) 的最大流是 \(f\),\(s\), \(t\) 所在的割集为 \(S\), \(T\),那么 \(\forall_{x \in S, y \in T}\), \(\operatorname{maxflow}(x

【BZOJ-2229】最小割 最小割树(最大流+分治)

2229: [Zjoi2011]最小割 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 1565  Solved: 560[Submit][Status][Discuss] Description 小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割. 对于带权图来说,将所有顶点处在不同部分的边的

[ZJOI2011] 最小割 - 最小割树

最小割树裸题 建树后,以每个点为根跑DFS求出距离矩阵,然后暴力回答询问即可 #include <bits/stdc++.h> using namespace std; #define int long long const int maxn=6e2; const int maxm=4e4; const int inf=1e13; int n,m,q; //for the target graph vector <pair<int,int> > g[maxn]; voi

【BZOJ-4435】Juice Junctions 最小割树(分治+最小割)+Hash

4435: [Cerc2015]Juice Junctions Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 20  Solved: 11[Submit][Status][Discuss] Description 你被雇佣升级一个旧果汁加工厂的橙汁运输系统.系统有管道和节点构成.每条管道都是双向的,且每条管道的流量都是1升每秒.管道可能连接节点,每个节点最多可以连接3条管道.节点的流量是无限的.节点用整数1到n来表示.在升级系统之前,你需要对现有

bzoj2229: [Zjoi2011]最小割(分治最小割+最小割树思想)

2229: [Zjoi2011]最小割 题目:传送门 题解: 一道非常好的题目啊!!! 蒟蒻的想法:暴力枚举点对跑最小割记录...绝对爆炸啊.... 开始怀疑是不是题目骗人...难道根本不用网络流???一看路牌....分治最小割?最小割树? 然后开始各种%论文... 简单来说吧,根据各种本蒟蒻不会证明的理论,那么:所有最小割都不是完全独立的,总共有n-1种(也就是树上的n-1条边)最小割 恰好和树的定义一样啊! 那么用一个solve递归函数来解决,一开始任意找两个点作为st和ed来最小割,然后分

bzoj2229: [Zjoi2011]最小割(最小割树)

传送门 这题是用最小割树做的(不明白最小割树是什么的可以去看看这一题->这里) 有了最小割树就很简单了……点数那么少……每次跑出一个最大流就暴力搞一遍就好了 1 //minamoto 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #include<algorithm> 6 #include<queue> 7 #define inf 0x3f3f3f3f 8 usi

[bzoj2229][Zjoi2011]最小割_网络流_最小割树

最小割 bzoj-2229 Zjoi-2011 题目大意:题目链接. 注释:略. 想法: 在这里给出最小割树的定义. 最小割树啊,就是这样一棵树.一个图的最小割树满足这棵树上任意两点之间的最小值就是原图中这两点之间的最小割. 这个性质显然是非常优秀的. 我们不妨这样假设,我么已经把最小割树求出来了,那么这个题就迎刃而解了. 我们可以直接枚举点对,然后暴力验证就可以直接枚举出所有的合法点对是吧. 那么问题来了,我们如何才能求出所有的合法的点对? 这就需要用到了最小割树的构建过程. 我们最小割树的构

Codeforces 343E 最小割树

题意及思路:https://www.cnblogs.com/Yuzao/p/8494024.html 最小割树的实现参考了这篇博客:https://www.cnblogs.com/coder-Uranus/p/9771919.html 代码: #include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; const int maxn = 210; const int maxm = 2010; int head[maxn