网络战争 [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])));
}
}
网络战争 [KD-Tree+最小割树]的更多相关文章
- bzoj 2229: [Zjoi2011]最小割【Gomory–Hu tree最小割树】
这个算法详见http://www.cnblogs.com/lokiii/p/8191573.html 求出两两之间最小割之后暴力统计即可 #include<iostream> #inclu ...
- (2016北京集训十三)【xsy1532】网络战争 - 最小割树+树上倍增+KD树
题解: 好题!! 这题似乎能上我代码长度记录的前五? 调试时间长度应该也能上前五QAQ 首先题目要求的明显就是最小割,当然在整个森林上求Q次最小割肯定是会GG的,所以我们需要一个能快速求最小割的算法— ...
- 最小割树(Gomory-Hu Tree)求无向图最小割详解 附 BZOJ2229,BZOJ4519题解
最小割树(Gomory-Hu Tree) 前置知识 Gomory-Hu Tree是用来解决无向图最小割的问题的,所以我们需要了解无向图最小割的定义 和有向图类似,无向图上两点(x,y)的割定义为一个边 ...
- LoibreOJ 2042. 「CQOI2016」不同的最小割 最小割树 Gomory-Hu tree
2042. 「CQOI2016」不同的最小割 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据 题目描述 ...
- [学习笔记]最小割树(Gomory-Hu Tree)
最小割树(\(\mathcal{Gomory-Hu Tree}\))简明指南 对于单源最短路径,我们有\(SPFA\)和\(Dijkstra\),对于多源最短路径,我们有\(Floyd\):对于两点间 ...
- 最小割树(Gomory-Hu Tree)
当我们遇到这样的问题: 给定一个 \(n\) 个点 \(m\) 条边的无向连通图,多次询问两点之间的最小割 我们通常要用到最小割树. 博客 建树 分治.记录当前点集,然后随便找俩点当 \(s\) 和 ...
- [模板]最小割树(Gomory-Hu Tree)(luogu4897)
给定一个\(n\)个点\(m\)条边的无向连通图,多次询问两点之间的最小割 两点间的最小割是这样定义的:原图的每条边有一个割断它的代价,你需要用最小的代价使得这两个点不连通 Input 第一行两个数\ ...
- 【模板】最小割树(Gomory-Hu Tree)
传送门 Description 给定一个\(n\)个点\(m\)条边的无向连通图,多次询问两点之间的最小割 两点间的最小割是这样定义的:原图的每条边有一个割断它的代价,你需要用最小的代价使得这两个点不 ...
- 【BZOJ-2229】最小割 最小割树(最大流+分治)
2229: [Zjoi2011]最小割 Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 1565 Solved: 560[Submit][Status ...
随机推荐
- jquery操作元素的位置
.offset() 在匹配的元素中,获取第一个元素的当前坐标,或设置每一个元素的坐标,坐标相对于文档. .offset() 这个不接受任何参数. var offset = p.offset(); // ...
- Element表单验证(1)
Element表单验证(1) 首先要掌握Element官方那几个表单验证的例子,然后才看下面的教程. Element主要使用了async-validator这个库作为表单验证 async-valida ...
- 在centos7云服务器上搭建Apache服务器并访问到你的网站
使用X-shell ssh安全连接到云服务器 https://mail.qq.com/cgi-bin/mail_spam?action=check_link&url=https://www.n ...
- centos7编译安装lamp实现wordpress
准备安装包,并解压 mariadb-10.3.13.tar.gz ,php-7.3.2.tar.bz2 ,httpd-2.4.38.tar.bz2 php-7.3.2 , phpMyAdmin ...
- intellij中导入java包
- JAVA / MySql 编程—— 第三章 高级查询(一)
1. 修改表: (1)修改表名语法: ALTER TABLE <旧表名> RENAME [ TO ] <新表名>: 注意:其中[TO]为可选参数,使用与否不影响结 ...
- php-5.6.26源代码 - PHP文件汇编成opcode、执行
文件 php-5.6.26/Zend/zend.c ZEND_API int zend_execute_scripts(int type TSRMLS_DC, zval **retval, int f ...
- mysql 按值排序
order by FIELD(AuditorStatus,3,0,1,2) laravel 写法 $data = $query->orderByRaw(\DB::Raw('FIELD(Audit ...
- MySQL基础 (麦子学员 php 第二阶段)
通过my.ini配置文件修改字符集:客户端字符集设置:[mysql]default-character-set=utf8 [mysqld] character-set-server=utf8 .设置之 ...
- anaconda+jupyter notebook 安装配置
安装Anaconda 从清华大学开源软件镜像站选择合适自己的版本 wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda ...