我曾经以为$LCT$已经足够毒瘤,直到我写了树套树。
我曾经又以为树套树已经足够毒瘤,直到我写了这道题。
$hhh$
这道题大概分为三个部分
一、首府之间最近点相连
由于$N$不超过$10^5$,且$|X_i|,|Y_i|$均匀随机,我们大可以使用$KD-Tree$,即先以每个州首府坐标建出$KD-Tree$,再枚举每一个点进行在$KD-Tree$中搜索,由于坐标随机,复杂度期望为$O(Nlog N)$。
这里有一个性质,那就是一定不存在重边以外的简单环。因为如果存在,那么你一定能找到一个不符合要求的点,所以这样连出来的一定是树或森林。如果有重边,那么很显然若我们要割掉它,必须把两条边全部割掉,即把所有重边变成一条边,这条新的边权值是原来重边的和。并且在后续计算答案时,若两个点所在的州不在同一个连通块上,那么答案为$0$,因为本身就不联通,这个用并查集维护即可。
二、预处理最小割树$(Gomoryspace Tree)$
最小割树基于一个非常重要的理论:在$n$个点的无向图中,本质不同的割至多有$n-1$个。利用这个性质,我们可以将这个无向图重构成一棵树,每一条树边代表一种割的方案的代价。
具体的方法就是,设一开始所有的点都在同一个点集中,然后随便选一个点数大于$1$的点集$S$,从$S$中任取两点$a,b$,以$a,b$为源汇跑最小割,然后把$S$中和$a$在割集同一侧的点单独拿出来,$S$中剩下的点一定和$b$在割集的同一侧,这样产生了两个$S$的非空子集$S_1,S_2$,并且在重构的树上建立一条$(a,b)$权值为割集大小的树边,然后将$S_1,S_2$递归处理直到$|S|=1$为止,这样,任意两个点最小割就是在重构树上两个点的路径上最小的边权。虽然说这样的复杂度可能达到$O(Dinicsum n_i^2)$,这道题并没有其他更可行的做法......,不过在我写到这里的时候,隔壁的$julao$ Mys_C_K 走了过来告诉我这个复杂度是可以用均值不等式来证明在$Dinic$够快的情况下是合法的。
三、大小树跑倍增
大树就是每一个首府连成的树,小树就是每个州内部的最小割树,只是用倍增维护一下路径最小值而已,非常简单,没啥可说的。
来吧,面对疾风吧。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define mid ((l+r)>>1) #define INF 1000000000 #define N 100200 #define HA main using namespace std; namespace IO{ const int SS=(1<<20)+5; char buf[SS],*H,*K; inline char Get(){ if(H==K) K=(H=buf)+fread(buf,1,SS,stdin); if(H==K) return -1;return *H++; } inline int read(){ int x=0,fg=1;char c=Get(); while(!isdigit(c)&&c!='-') c=Get(); if(c=='-') fg=-1,c=Get(); while(isdigit(c)) x=x*10+c-'0',c=Get(); return x*fg; } char obuf[SS],*oS=obuf,*oT=oS+SS-1,c,qu[55];int qr; inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;} inline void putc(char x){*oS++ =x;if(oS==oT) flush();} void print(int x){ if(!x) putc('0'); if(x<0) putc('-'),x=-x; while(x) qu[++qr]=x%10+'0',x/=10; while(qr) putc(qu[qr--]); putc(' '); } } using namespace IO; int sq(int x){return x*x;} int n,m,fs[N],nt[N<<1],to[N<<1],len[N<<1],dt[N],ot[N],vis[N],kd; int u[N<<6],v[N<<6],w[N<<6],V[N][2],E[N][2],tmp,now; int res[N],G[N][18],P[N][18],D[N]; struct node{ int id,c[2]; node(){} node(int _id,int _c0,int _c1){id=_id,c[0]=_c0,c[1]=_c1;} void gtin(int x){c[0]=read(),c[1]=read(),id=x;} bool operator <(const node&ot)const{return c[kd]<ot.c[kd];} }p[N]; int dst(node x,node y){return sq(x.c[0]-y.c[0])+sq(x.c[1]-y.c[1]);} struct TREE{ int mi[N][2],mx[N][2],cnt,t[N],fa[N][18],F[N],W[N][18],L[N],R[N]; int fst[N],nxt[N<<1],gto[N<<1],val[N],dis[N<<1],TMP,dep[N],Root; int fd(int x){return F[x]==x?x:F[x]=fd(F[x]);} void gt(int x,node k){mx[x][0]=mi[x][0]=k.c[0],mx[x][1]=mi[x][1]=k.c[1];} void iit(){cnt=TMP=0,memset(fst,-1,sizeof(fst)),memset(W,0x3f,sizeof(W));} int mind(int x,node k){ if(!x) return INF; int tt=0; for(int i=0;i<2;i++){ if(k.c[i]<mi[x][i]) tt+=sq(mi[x][i]-k.c[i]); else if(k.c[i]>mx[x][i]) tt+=sq(mx[x][i]-k.c[i]); } return tt; } void update(int x,int from){ if(from==0) return; mx[x][0]=max(mx[x][0],mx[from][0]),mx[x][1]=max(mx[x][1],mx[from][1]); mi[x][0]=min(mi[x][0],mi[from][0]),mi[x][1]=min(mi[x][1],mi[from][1]); } void pushup(int x){gt(x,p[t[x]]),update(x,L[x]),update(x,R[x]);} void build(int &x,int l,int r,int nw){ x=++cnt,L[x]=R[x]=0; m=nw,nth_element(p+l,p+mid,p+r+1);t[x]=mid,gt(x,p[t[x]]); if(l<mid) build(L[x],l,mid-1,nw^1),update(x,L[x]); if(mid<r) build(R[x],mid+1,r,nw^1),update(x,R[x]); } void query(int x,int ps){ if(!x) return; if(t[x]!=ps){ int t1=dst(p[ps],p[t[x]]); if(t1<dt[p[ps].id]) dt[p[ps].id]=t1,ot[p[ps].id]=p[t[x]].id; else if(t1==dt[p[ps].id]&&p[t[x]].id<ot[p[ps].id]) ot[p[ps].id]=p[t[x]].id; } int t1=mind(L[x],p[ps]),t2=mind(R[x],p[ps]); if(t1<t2){if(t1<=dt[p[ps].id]) query(L[x],ps);if(t2<=dt[p[ps].id]) query(R[x],ps);} else{if(t2<=dt[p[ps].id]) query(R[x],ps);if(t1<=dt[p[ps].id]) query(L[x],ps);} } void lk(int x,int y,int Len){nxt[TMP]=fst[x],fst[x]=TMP,gto[TMP]=y,dis[TMP++]=Len;} void DFS(int x,int last,int from){ fa[x][0]=last,dep[x]=dep[last]+1,W[x][0]=from; for(int j=1;j<18;j++){ fa[x][j]=fa[fa[x][j-1]][j-1]; W[x][j]=min(W[x][j-1],W[fa[x][j-1]][j-1]); } for(int i=fst[x];i!=-1;i=nxt[i]) if(gto[i]!=last) DFS(gto[i],x,dis[i]); } void cover(){ for(int i=1;i<=n;i++) fst[i]=-1,F[i]=i,query(Root,i); for(int i=1;i<=n;i++){ if(i==ot[ot[i]]&&val[ot[i]]>0) val[ot[i]]+=val[i],val[i]=0; else lk(i,ot[i],val[i]),lk(ot[i],i,val[i]),F[fd(i)]=fd(ot[i]); } for(int i=1;i<=n;i++) if(fd(i)==i) DFS(i,0,0); } int minn(int x,int y){ if(fd(x)!=fd(y)) return 0; if(dep[x]<dep[y]) swap(x,y); int now=INF; for(int j=17;j>=0;j--){ if(dep[fa[x][j]]<dep[y]) continue; now=min(now,W[x][j]),x=fa[x][j]; } for(int j=17;j>=0;j--){ if(fa[x][j]==fa[y][j]) continue; now=min(now,min(W[x][j],W[y][j])); x=fa[x][j],y=fa[y][j]; } while(x!=y) now=min(now,min(W[x][0],W[y][0])),x=fa[x][0],y=fa[y][0]; return now; } void RUN(){build(Root,1,n,0);cover();} }Cap; struct Gomory_tree{ int cur[N],S,T,rem[N],q[N],hd,tl,tt[N],pos[N],col[N],tot,sz,nw; void addedge(int x,int y,int rm){ nt[tmp]=fs[x],fs[x]=tmp,to[tmp]=y,rem[tmp++]=rm; nt[tmp]=fs[y],fs[y]=tmp,to[tmp]=x,rem[tmp++]=rm; } void init(int B){ tot=E[B][1]-E[B-1][1],sz=V[B][1]-V[B-1][1]; for(int i=1;i<=sz;i++) fs[i]=-1,pos[i]=i; tmp=0,nw=E[B][0]; for(int i=E[B][0];i<=E[B][1];i++) addedge(u[i],v[i],w[i]); } bool BFS(){ for(int i=1;i<=sz;i++) cur[i]=fs[i],vis[i]=-2; hd=tl=0,vis[S]=1,q[tl++]=S; while(hd<tl){ int x=q[hd++]; for(int i=fs[x];i!=-1;i=nt[i]){ if(rem[i]==0||vis[to[i]]>=0) continue; vis[to[i]]=vis[x]+1,q[tl++]=to[i]; } } return vis[T]>0; } int dfs(int x,int mxf){ if(x==T||!mxf) return mxf; int temp=0; for(int &i=cur[x];i!=-1;i=nt[i]){ if(rem[i]==0||vis[to[i]]!=vis[x]+1) continue; int lc=min(mxf-temp,rem[i]); int rc=dfs(to[i],lc); rem[i]-=rc,rem[i^1]+=rc,temp+=rc; if(temp==mxf) break; } if(temp==0) vis[x]=-2; return temp; } int dinic(){int as=0;while(BFS()) as+=dfs(S,INF);return as;} void clr(){ for(int i=0,sum;i<tmp;i+=2) sum=rem[i]+rem[i+1],rem[i]=rem[i+1]=(sum>>1); for(int i=1;i<=sz;i++) col[i]=0; } void Paint(int x){ if(col[x]) return; col[x]=1; for(int i=fs[x];i!=-1;i=nt[i]) if(rem[i]) Paint(to[i]); } void solve(int l,int r){ if(l==r) return; clr(); S=pos[l],T=pos[r],w[nw]=dinic(); int t1=l,t2=r; u[nw]=S,v[nw]=T,nw++; Paint(S); for(int i=l;i<=r;i++){ if(col[pos[i]]) tt[t1++]=pos[i]; else tt[t2--]=pos[i]; } for(int i=l;i<=r;i++) pos[i]=tt[i]; solve(l,t2),solve(t1,r); } void check(int x){init(x),solve(1,sz);} }Cut; void getdep(int x,int last,int from){ res[x]=min(res[last],from); P[x][0]=last,G[x][0]=from,D[x]=D[last]+1; for(int j=1;j<18;j++){ P[x][j]=P[P[x][j-1]][j-1]; G[x][j]=min(G[x][j-1],G[P[x][j-1]][j-1]); } for(int i=fs[x];i!=-1;i=nt[i]) if(to[i]!=last) getdep(to[i],x,len[i]); } int getans(int B1,int t1,int B2,int t2){ if(Cap.fd(B1)!=Cap.fd(B2)) return 0; if(B1==B2){ int fin=INF,x=t1+V[B1-1][1],y=t2+V[B1-1][1]; if(D[x]<D[y]) swap(x,y); for(int j=17;j>=0;j--){ if(D[P[x][j]]<D[y]) continue; fin=min(fin,G[x][j]),x=P[x][j]; } for(int j=17;j>=0;j--){ if(P[x][j]==P[y][j]) continue; fin=min(fin,min(G[x][j],G[y][j])); x=P[x][j],y=P[y][j]; } if(x!=y) fin=min(fin,min(G[x][0],G[y][0])); return fin; } int fin=min(res[t1+V[B1-1][1]],res[t2+V[B2-1][1]]); return min(fin,Cap.minn(B1,B2)); } int HA(){ n=read(),memset(fs,-1,sizeof(fs)); memset(dt,0x3f,sizeof(dt)),Cap.iit(); for(int i=1;i<=n;i++){ p[i].gtin(i),Cap.val[i]=read(); V[i][0]=V[i][1]=V[i-1][1];E[i][0]=E[i][1]=E[i-1][1]; V[i][0]++,E[i][0]++,V[i][1]+=read(),E[i][1]+=read(),res[V[i][0]]=INF; for(int j=E[i][0];j<=E[i][1];j++) u[j]=read(),v[j]=read(),w[j]=read(); } Cap.RUN(),res[0]=INF; for(int i=1;i<=n;i++) Cut.check(i); memset(fs,-1,sizeof(fs)),tmp=0; for(int i=1;i<=n;i++){ for(int j=E[i][0];j<E[i][0]+V[i][1]-V[i][0];j++){ u[j]+=V[i-1][1],v[j]+=V[i-1][1]; nt[tmp]=fs[u[j]],fs[u[j]]=tmp,to[tmp]=v[j],len[tmp++]=w[j]; nt[tmp]=fs[v[j]],fs[v[j]]=tmp,to[tmp]=u[j],len[tmp++]=w[j]; } getdep(V[i][0],0,INF); } for(int Q=read(),t1,t2,b1,b2,now;Q;Q--,tmp=0){ b1=read(),b2=read(),t1=read(),t2=read(); now=getans(b1,t1,b2,t2),print(now); }flush(); return 0; }