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

题面

思路

首先吐槽一下:

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

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

然后是正经东西

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

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

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

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

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

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

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

扫描二维码关注公众号,回复: 3289867 查看本文章

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

求最小割树的方法可以参考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])));
    }
}

猜你喜欢

转载自www.cnblogs.com/dedicatus545/p/9690255.html