zoukankan      html  css  js  c++  java
  • 网络战争 [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
    ",orig::query(cap[t1]+t3,cap[t1]+t4));
            else printf("%d
    ",min(bigg::query(t1,t2),min(orig::dis[cap[t1]+t3],orig::dis[cap[t2]+t4])));
        }
    }
    
  • 相关阅读:
    动态网站项目(Dynamic Web Project)CRUD(增删改查)功能的实现(mvc(五层架构)+jdbc+servlet+tomcat7.0+jdk1.8),前端使用JSP+JSTL+EL组合
    动态网站项目(Dynamic Web Project)登录功能的实现(mvc(五层架构)+jdbc+servlet+tomcat7.0+jdk1.8)(js验证+cookie)
    Oracle数据库scott用户无法导入数据的解决方法
    Win10安装Oracle 11g后解决sqldeveloper缺少快捷方式的问题
    【转载】win10系统安装oracle11g详细步骤
    WPF学习笔记(8):DataGrid单元格数字为空时避免验证问题的解决
    WPF学习笔记(7):DataGrid中数字自定义格式显示
    WPF学习笔记(6):DataSet更新后台数据库个别列失败的问题
    WPF学习笔记(5):两个DataGrid的滚动条实现同步滚动
    WPF学习笔记(4):获取DataGridTemplateColumn模板定义的内容控件
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/9690255.html
Copyright © 2011-2022 走看看