【2016北京集训】网络战争
Portal -->broken qwq
Description
有\(N\)个州,每个州内有一个网络系统,有\(m_i\)条网络线路连接州内的城市(每个州内城市从\(1\)到\(n_i\)编号),每个州中的\(1\)号城市被成为首府,首府有坐标,每个州的首府都会有一条线路连接到距离最近的另一个州的首府,破坏每条线路有一定的代价,现在给出若干询问:求断开两个指定城市的网络连接需要的最小代价
数据范围:\(2<=N<=50000,\sum n_i<=100000,1<=n_i<=2000,1<=Q<=100000\)
Solution
好像是第一次写那么长的代码qwq写完调完整个人都不好了(这道题拼得还真是直白qwq)虽然说貌似有dalao们4到5k就过了==
虽然说好像思路比较简单粗暴但是。。第一次写最小割树还要是第一份9k所以还是丢上来纪念一下好了qwq
先不考虑时间复杂度的问题,注意到断开两个指定城市有几种情况:
(1)这两个城市在同一个州内:那么最暴力的处理方式就是把这个州的图建出来然后跑最小割
(2)这两个城市不在同一个州内:那么我们考虑一下不同州之间的连边:可以发现首府之间的连边最后连出来应该是。。若干个基环树,并且这些环的长度都是\(2\),那么这个时候我们又可以分两种情况讨论:
1、如果说两个城市的州在同一个基环树上,那么我们可以选择断掉城市与首府的连边,或者断掉基环树上的连边,而基环树上的连边又分两种:同时断掉环上的两条边,或者断掉树边
2、如果说两个城市的州不在同一个基环树上,那。。本来就不是连通的了不用管
那么现在看我们需要快速处理的东西:找最近点可以用kd-tree,断掉树边可以用倍增找最小值,环的话可以预处理,最后剩下的就是最小割
实际上最小割也可以预处理
这里用到一个叫做最小割树的东西。实际上这个玩意。。就是套一个分治的思想,大致过程如下:
我们考虑对于当前的图随便找两个点作为\(S\)和\(T\)跑一次最小割,然后将图中的点分为\(S\)集和\(T\)集,取\(S\)集中随便一个点和\(T\)集中随便一个点(或者直接取\(S\)和\(T\)就好了看个人习惯),给这两个点连一条长度为最小割的边,然后再在\(S\)集中选出新的源和新的汇,在\(T\)集中选出新的源和新的汇,分别递归处理,直到当前点集中只有一个点
这样我们会建出一棵树,而两个点的最小割就是这两个点在树上的路径上边权最小值
具体为什么的话。。画个图就很好理解了,其实形象一点来说就是把这个图分成一段一段的,然后只要在两个点之间的任意一段断开就好了
时间复杂度的话。。就是\(n\)次网络流的复杂度
于是这个最小割的问题在建出了最小割树之后我们也可以用树上倍增直接处理了
于是我们要做的就是把这堆东西全部预处理出来然后快乐查询就好了(说着好轻松啊)
然后我用我挫挫的码风就写了9k。。。棒
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define ll long long
#define Pr pair<int,int>
#define mp make_pair
using namespace std;
const int N=50010,TOP=20,inf=2147483647;
int Which;
ll sqr(ll x){return x*x;}
struct Rec{
int data[2],id,id1;
friend bool operator <(Rec x,Rec y){return x.data[Which]<y.data[Which];}
friend ll get_dis(Rec x,Rec y){return sqr(x.data[0]-y.data[0])+sqr(x.data[1]-y.data[1]);}
}dot[N],aim;
struct Rec2{
int x,y,z;
void read(){scanf("%d%d%d",&x,&y,&z);}
}edge[2010];
int n[N],m[N],v[N],tg[N];
int id[N],mark;
int n_all,m_all,cir,Q;
ll ans,ansid;
namespace Kd{/*{{{*/
int ch[N][2],mxx[N],mxy[N],mnx[N],mny[N];
vector<int> rec[N];
int rt;
void update(int x,int son){
mxx[x]=max(mxx[x],mxx[son]);
mnx[x]=min(mnx[x],mnx[son]);
mxy[x]=max(mxy[x],mxy[son]);
mny[x]=min(mny[x],mny[son]);
}
void pushup(int x){
mnx[x]=mxx[x]=dot[x].data[0];
mny[x]=mxy[x]=dot[x].data[1];
if (ch[x][0]) update(x,ch[x][0]);
if (ch[x][1]) update(x,ch[x][1]);
}
int _build(int l,int r,int now){
if (l>r) return 0;
int mid=l+r>>1;
Which=now;
nth_element(dot+l,dot+mid,dot+1+r);
ch[mid][0]=_build(l,mid-1,now^1);
ch[mid][1]=_build(mid+1,r,now^1);
pushup(mid);
return mid;
}
void build(int n){rt=_build(1,n,0);}
ll get_dis(int who,ll x,ll y){
ll xx=0,yy=0;
if (mxx[who]<x) xx=x-mxx[who];
if (mnx[who]>x) xx=mnx[who]-x;
if (mxy[who]<y) yy=y-mxy[who];
if (mny[who]>y) yy=mny[who]-y;
return sqr(xx)+sqr(yy);
}
void query(int x){
if (!x) return;
ll ld=inf,rd=inf,d=get_dis(dot[x],aim);
if (dot[x].id!=aim.id&&(ans>d||(ans==d&&dot[x].id<ansid)))
ans=d,ansid=dot[x].id;
if (ch[x][0])
ld=get_dis(ch[x][0],aim.data[0],aim.data[1]);
if (ch[x][1])
rd=get_dis(ch[x][1],aim.data[0],aim.data[1]);
if (ld<rd){
if (ld<=ans) query(ch[x][0]);
if (rd<=ans) query(ch[x][1]);
}
else{
if (rd<=ans) query(ch[x][1]);
if (ld<=ans) query(ch[x][0]);
}
}
}/*}}}*/
namespace F{/*{{{*/
const int M=1e5+10,N=2010;
struct xxx{
int y,nxt,r;
}a[M*2];
queue<int> q;
int h[N],lv[N],vis[N];
int tot,S,T,n;
void init(int _n){
memset(h,-1,sizeof(h));
tot=-1; n=_n;
}
void add(int x,int y,int r){
a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot; a[tot].r=r;
a[++tot].y=x; a[tot].nxt=h[y]; h[y]=tot; a[tot].r=0;
}
bool bfs(){
int u,v;
while (!q.empty()) q.pop();
for (int i=1;i<=n;++i) lv[i]=0;
q.push(S); lv[S]=1;
while (!q.empty()){
v=q.front(); q.pop();
for (int i=h[v];i!=-1;i=a[i].nxt){
u=a[i].y;
if (!a[i].r||lv[u]) continue;
lv[u]=lv[v]+1;
q.push(u);
if (u==T) return true;
}
}
return false;
}
int dfs(int v,int o){
if (v==T||!o) return o;
int u;
int flow,ret=0;
for (int i=h[v];i!=-1;i=a[i].nxt){
u=a[i].y;
if (lv[u]!=lv[v]+1||!a[i].r) continue;
flow=dfs(u,min(o,a[i].r));
if (flow){
o-=flow;
ret+=flow;
a[i].r-=flow;
a[i^1].r+=flow;
if (!o) break;
}
}
if (!ret) lv[v]=-1;
return ret;
}
int dinic(){
int ret=0;
while (bfs())
ret+=dfs(S,inf);
return ret;
}
void find(int &ret,int op,int mark){//0=in S 1=in T
for (int i=1;i<=n;++i){
if (id[i]!=mark) continue;
if (op){
if (!lv[i]&&i!=T){ret=i; return;}
}
else
if (lv[i]&&i!=S){ret=i; return;}
}
}
void remark(int mark,int nwmark,int op,int *Lv){//0=S 1=T
for (int i=1;i<=n;++i){
if (id[i]!=mark) continue;
if (op){
if (!Lv[i]) id[i]=nwmark;
}
else
if (Lv[i]) id[i]=nwmark;
}
}
}/*}}}*/
namespace Inner{/*{{{*/
const int N=1e5+10,TOP=20;
struct xxx{
int y,nxt,dis;
}a[N*2];
int pre[N][TOP+1],mn[N][TOP+1];
int h[N],rt[::N],dep[N];
int tot;
void first_init(){
memset(h,-1,sizeof(h));
tot=0;
}
void init(int x){rt[x]=tg[x]+1;}
void add(int x,int y,int d){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot; a[tot].dis=d;}
void _dfs(int fa,int x,int d){
int u;
dep[x]=d; pre[x][0]=fa;
for (int i=1;i<=TOP;++i){
pre[x][i]=pre[pre[x][i-1]][i-1];
mn[x][i]=min(mn[x][i-1],mn[pre[x][i-1]][i-1]);
}
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa) continue;
mn[u][0]=a[i].dis;
_dfs(x,u,d+1);
}
}
void dfs(int x){_dfs(0,rt[x],1);}
int get_mn(int x,int y){
int ret=inf;
if (x==y) return ret;
if (dep[x]<dep[y]) swap(x,y);
for (int i=TOP;i>=0;--i)
if (dep[pre[x][i]]>=dep[y]){
ret=min(mn[x][i],ret);
x=pre[x][i];
}
if (x==y) return ret;
for (int i=TOP;i>=0;--i)
if (pre[x][i]!=pre[y][i]){
ret=min(mn[x][i],min(mn[y][i],ret));
x=pre[x][i],y=pre[y][i];
}
ret=min(mn[x][0],min(mn[y][0],ret));
return ret;
}
}/*}}}*/
//build_Inner{{{
void build(int S,int T,int which,int now){
int tmprec[2010];
F::S=S; F::T=T;
F::init(n[which]);
for (int i=1;i<=m[which];++i){
//if (id[edge[i].x]==mark&&id[edge[i].y]==mark){
F::add(edge[i].x,edge[i].y,edge[i].z);
F::add(edge[i].y,edge[i].x,edge[i].z);
//}
}
int tmp=F::dinic(),nwT=-1,nwS=-1;
F::find(nwT,0,now);
F::find(nwS,1,now);
Inner::add(S+tg[which],T+tg[which],tmp);
Inner::add(T+tg[which],S+tg[which],tmp);
for (int i=1;i<=n[which];++i) tmprec[i]=F::lv[i];
F::remark(now,++mark,0,tmprec);
if (nwT!=-1) build(S,nwT,which,mark);
F::remark(now,++mark,1,tmprec);
if (nwS!=-1) build(nwS,T,which,mark);
}
void get_tree(int which){
int x,y,z;
for (int i=1;i<=m[which];++i) edge[i].read();
memset(id,0,sizeof(id));
mark=0;
Inner::init(which);
build(1,n[which],which,0);
int debug=1;
Inner::dfs(which);
}/*}}}*/
namespace Cap{/*{{{*/
const int N=1e5+10,TOP=20;
struct xxx{
int y,nxt,dis;
}a[N*2];
Pr rec_cir[N/2];
int h[N],to[N],bl[N],dep[N],rt[N];
int pre[N][TOP+1],mn[N][TOP+1],val[N];
int cir,tot;
void add(int x,int y,int d){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot; a[tot].dis=d;}
void build(){
Kd::build(n_all);
for (int i=1;i<=n_all;++i){
ans=1LL<<60; ansid=ans;
aim=dot[i];
Kd::query(Kd::rt);
to[dot[i].id]=ansid;
}
memset(h,-1,sizeof(h));
memset(rt,0,sizeof(rt));
tot=0; cir=0;
int x,y;
for (int i=1;i<=n_all;++i){
x=dot[i].id; y=dot[i].id1;
val[dot[i].id]=v[y];
if (to[to[x]]==x&&x<to[x])
rec_cir[++cir]=mp(x,to[x]),rt[to[x]]=1,rt[x]=1;
else if (to[to[x]]!=x){
add(x,to[x],v[y]); add(to[x],x,v[y]);
}
}
}
void dfs(int fa,int x,int which,int d){
int u;
dep[x]=d; pre[x][0]=fa; bl[x]=which;
for (int i=1;i<=TOP;++i){
pre[x][i]=pre[pre[x][i-1]][i-1];
mn[x][i]=min(mn[x][i-1],mn[pre[x][i-1]][i-1]);
}
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||rt[u]) continue;
mn[u][0]=a[i].dis;
dfs(x,u,which,d+1);
}
}
void presolve(){
build();
for (int i=1;i<=cir;++i){
dfs(0,rec_cir[i].first,rec_cir[i].first,1);
dfs(0,rec_cir[i].second,rec_cir[i].second,1);
}
}
int get_mn(int x,int y){
int ret=inf;
if (x==y) return ret;
if (dep[x]<dep[y]) swap(x,y);
for (int i=TOP;i>=0;--i)
if (dep[pre[x][i]]>=dep[y]){
ret=min(mn[x][i],ret);
x=pre[x][i];
}
if (x==y) return ret;
for (int i=TOP;i>=0;--i)
if (pre[x][i]!=pre[y][i]){
ret=min(mn[x][i],min(mn[y][i],ret));
x=pre[x][i],y=pre[y][i];
}
ret=min(mn[x][0],min(mn[y][0],ret));
return ret;
}
bool link(int x,int y){return (bl[x]==bl[y])||(to[bl[x]]==bl[y]);}
bool cmp(Rec x,Rec y){return x.id1<y.id1;}
void debug(){
sort(dot+1,dot+1+n_all,cmp);
for (int i=1;i<=n_all;++i) printf("%d %d\n",to[dot[i].id],dot[i].id);
}
int query(int x,int y){
int ret=min(get_mn(x,bl[x]),get_mn(y,bl[y]));
if (bl[x]!=bl[y]) return min(ret,val[bl[x]]+val[bl[y]]);
return get_mn(x,y);
}
}/*}}}*/
void solve(){
int ta,tb,qa,qb,ans;
scanf("%d",&Q);
for (int i=1;i<=Q;++i){
scanf("%d%d%d%d\n",&ta,&tb,&qa,&qb);
ans=inf;
if (ta==tb){
ans=Inner::get_mn(qa+tg[ta],qb+tg[tb]);
}
else{
if (!Cap::link(1+tg[ta],1+tg[tb])) ans=0;
else{
ans=min(Inner::get_mn(qa+tg[ta],1+tg[ta]),Inner::get_mn(qb+tg[tb],1+tg[tb]));
ans=min(ans,Cap::query(1+tg[ta],1+tg[tb]));
}
}
printf("%d\n",ans);
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d",&n_all);
Inner::first_init();
for (int i=1;i<=n_all;++i){
scanf("%d%d\n",&dot[i].data[0],&dot[i].data[1]);
tg[i]=tg[i-1]+n[i-1];
dot[i].id=tg[i]+1;
dot[i].id1=i;
scanf("%d\n",&v[i]);
scanf("%d%d\n",&n[i],&m[i]);
get_tree(i);
}
Cap::presolve();
//Cap::debug();//return 0;
solve();
}