网络战争 [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 ...
随机推荐
- 由fastRPC产生的DB服务
根据整理的RPC模型,在此上,根据最近的项目,发布了DB服务,操作数据库.以RPC模型,发布数据库的操作服务,主要发送SQL语句,在服务端执行:同时引入了流行的数据库连接池:服务端还发布了文件接收服务 ...
- LeetCode105. Construct Binary Tree from Preorder and Inorder Traversal
题目 根据一棵树的前序遍历与中序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3 ...
- Q&A - Nginx与Tomcat的区别?
web上的server都叫web server,但是大家分工也有不同的. nginx常用做静态内容服务和代理服务器(不是你FQ那个代理),直面外来请求转发给后面的应用服务(tomcat,django什 ...
- 安装破解IDEA(个人使用)
安装的过程,许多的教程都会有,我在这里附上一两个链接吧:https://blog.csdn.net/newabcc/article/details/80601933 他这里也有破解过程,但是比较麻烦, ...
- 标签种类及CSS引入方法
标签种类及CSS引入方法 标签种类: 一:块级标签(block) ——> 独占一行,默认宽度与内容无关,宽高可设 (hr 块级标签) 二:行内块标签(inline-block) ——> ...
- python的运行过程剖析·编程语言分类
总结: 编程语言的分类 编译型: 说明:与汇编语言类似,都有一个编译程序将源代码编译成硬件可执行的二进制代码 特点:执行速度快.同等情况下对系统要求低,适合于开发大型应用程序.数据库系统.操作系统等 ...
- iOS常用控件-UIScrollView
一. 常见属性 @property (nonatomic) CGPoint contentOffset; //记录UIScrollView滚动的位置 @pro ...
- [Bzoj3991]寻宝游戏(dfs序+set)
Description 题目链接 Solution 用set按dfs序维护当前的宝物序列,那么答案为相邻2个点的距离加上头尾2个的距离 Code #include <cstdio> #in ...
- 笔记-scrapy-辅助功能
笔记-scrapy-辅助功能 1. scrapy爬虫管理 爬虫主体写完了,要部署运行,还有一些工程性问题: 限频 爬取深度限制 按条件停止,例如爬取次数,错误次数: 资源使用限制,例如内存限 ...
- aspx页面 按钮不响应回车键
aspx页面在IE浏览器中,页面上的按钮默认都响应回车键,但有的时候我们的文本框可能需要响应回车键,这时我们就不想让按钮再响应回车键,这时我们只需要设置按钮的属性即可. 按钮分为两种,一种是<b ...