[2016北京集训试题6]网络战争-[最小割树(网络流)+kd-tree+倍增]

Description

A 联邦国有 N 个州,每个州内部都有一个网络系统,有若干条网络线路,连接各个 州内部的城市。 由于 A 国的州与州之间的关系不是太好,每个州都只有首府建立了到别的州的网络。具体来说,每个州的首府都只主动地建立了一条网络线路,连接到距离最近的州的 首府。(欧氏距离。如果有多个,选择标号最小的去连接) B 国探知了 A 国的网络线路分布情况,以及攻陷每条网络线路所需花费的代价,B 国首脑想知道断开 A 国某两个城市之间的网络连接,所需的最少代价。请你计算出来告 诉他。 注:两个城市之间可以有多条网络线路。可能有两个首府的坐标相同。

【输入格式】

第一行有一个正整数 N,表示 A 国州的个数。

接下来 N 个部分,每个部分描述每个州的情况: 第一行两个整数????,????,表示第 i 个州的首府坐标。

第二行一个正整数????,表示攻陷这个州主动建立的网络线路的代价。

第三行两个正整数????,????,表示第 i 个州有????个城市,????条网络线路。

接下来????行,每行三个数????,????,????,表示第 i 个州中城市????和????之间有一条网络线路,攻陷它的代价为????。

编号为 1 的城市,是这个州的首府。

接下来一行一个正整数 Q,表示询问个数。

接下来 Q 行,每行四个数 ta,tb,qa,qb,表示询问断开第 ta 个州第 qa 个城市和 第 tb 个州第 qb 个城市的最小代价。

【输出格式】 输出 Q 行,每行一个数,表示一组询问的答案

Solution

码农题啊啊啊啊啊

先用kd-tree求出每个首府向外连接的的点。可以证明不会出现>=3个节点的环。证明如下:

假如有一个3个节点的环a->b->c->a。(这里画成有向边的意思是为了清晰表示哪条边是哪个首府主动建的)

由于a->b,则dis(a,b)<=dis(a,c)。

同理dis(c,a)<=dis(b,c)<=dis(a,b)<=dis(a,c)。

所以dis(a,b)=dis(a,c)=dis(b,c)。

然后我们就会发现,不论怎么给a,b,c编号,都无法满足要求了。

接下来,直接用最小割树求出最小割,每次查询在树上倍增。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<map>
using namespace std;
const int inf=1e9;
int n,st[50010];
struct pas{int y,nxt,w,op;};
struct Gomory_Hu
{
    pas g[300010];int tot,h[100010];
    int fa[100010][18],mn[100010][18],dep[100010];
    void add(int x,int y,int w)
    {g[++tot]=pas{y,h[x],w,0};h[x]=tot;
     g[++tot]=pas{x,h[y],w,0};h[y]=tot;}
    void pre(int x,int f,int c)
    {
        fa[x][0]=f;mn[x][0]=c;dep[x]=dep[f]+1;
        for (int i=1;i<=16;i++)
        {
            fa[x][i]=fa[fa[x][i-1]][i-1];
            mn[x][i]=min(mn[x][i-1],mn[fa[x][i-1]][i-1]);
        }
        for (int i=h[x];i;i=g[i].nxt) if (g[i].y!=f)pre(g[i].y,x,g[i].w);
    }
    int asklca(int x,int y)
    {
        int ret=inf;
        if (dep[x]<dep[y]) swap(x,y);
        for (int i=17;i>=0;i--) if (dep[fa[x][i]]>dep[y]) ret=min(ret,mn[x][i]),x=fa[x][i];
        if (dep[x]>dep[y]) ret=min(ret,mn[x][0]),x=fa[x][0];
        if (x==y) return ret;
        for (int i=17;i>=0;i--) if (fa[x][i]!=fa[y][i])
        ret=min(ret,min(mn[x][i],mn[y][i])),x=fa[x][i],y=fa[y][i];
        ret=min(ret,min(mn[x][0],mn[y][0]));
        return ret;
    }
}gh;
struct Dinic
{
    pas g[8000010];
    int h[100010],tot=0,S,T;
    int reid[8000010],reflow[8000010],pos;
    int dep[100010];
    queue<int>q;
    bool bfs(int _i)
    {
        int x;
        memset(dep+st[_i],0,sizeof(int)*(st[_i+1]-st[_i]+1));dep[S]=1;
        while (!q.empty()) q.pop();
        q.push(S);
        while (!q.empty())
        {
            x=q.front();q.pop();
            for (int i=h[x];i;i=g[i].nxt)
            if (!dep[g[i].y]&&g[i].w)
            {
                dep[g[i].y]=dep[x]+1;
                q.push(g[i].y);
                if (g[i].y==T) return 1;
            }
        }
        return 0;
    }
    int dfs(int x,int flow)
    {
        if (x==T||(!flow))return flow;
        int tmp=0,js;
        for (int i=h[x];i;i=g[i].nxt)
        if (dep[g[i].y]==dep[x]+1&&g[i].w)
        {
            js=dfs(g[i].y,min(flow,g[i].w));
            if (js)
            {
                g[i].w-=js;
                g[g[i].op].w+=js;
                tmp+=js;
                flow-=js;
                reid[++pos]=i;reflow[pos]=js;

                if (!flow) return tmp;
            }
        }
        if (flow) dep[x]=-1;
        return tmp;
    }
    void add(int x,int y,int w)//无向图!
    {
        g[++tot]=pas{y,h[x],w,tot+1};h[x]=tot;
        g[++tot]=pas{x,h[y],w,tot-1};h[y]=tot;
    }
    int _in[100010],_flow[100010];
    void get_gh(int _i,int l,int r)
    {
        for (int i=l;i<=r;i++) _in[i]=l;
        for (int i=l+1;i<=r;i++)
        {
            S=i;T=_in[i];
            while (bfs(_i)) _flow[i]+=dfs(S,inf);
            for (int j=i+1;j<=r;j++) if (_in[i]==_in[j]&&dep[j]) _in[j]=i;
            while (pos)
            {
                g[reid[pos]].w+=reflow[pos];
                g[g[reid[pos]].op].w-=reflow[pos];pos--;
            }
        }
        for (int i=l+1;i<=r;i++) gh.add(i,_in[i],_flow[i]),_flow[i]=0;
    }
}D;

int v[50010];
map<int,int>mp[50010];
int rt,nw;
struct node{int t[2],id;
friend bool operator<(node a,node b){return a.t[nw]<b.t[nw];}
}p[100010],_p[100010];
struct KD_TREE
{
    int up[100010],down[100010],lft[100010],rght[100010];
    int lc[100010],rc[100010],cnt=0;
    void pushup(int k)
    {
        if (lc[k])
        {
            up[k]=max(up[k],up[lc[k]]);
            down[k]=min(down[k],down[lc[k]]);
            lft[k]=min(lft[k],lft[lc[k]]);
            rght[k]=max(rght[k],rght[lc[k]]);
        }
        if (rc[k])
        {
            up[k]=max(up[k],up[rc[k]]);
            down[k]=min(down[k],down[rc[k]]);
            lft[k]=min(lft[k],lft[rc[k]]);
            rght[k]=max(rght[k],rght[rc[k]]);
        }
    }
    int build(int _now,int l,int r)
    {
        if (l>r) return 0;
        int mid=(l+r)/2;nw=_now;
        nth_element(p+l,p+mid+1,p+r+1);
        up[mid]=down[mid]=p[mid].t[1];
        lft[mid]=rght[mid]=p[mid].t[0];
        lc[mid]=build(_now^1,l,mid-1);
        rc[mid]=build(_now^1,mid+1,r);
        pushup(mid);
        return mid;
    }
    int dis(node a,node b)
    {return (a.t[0]-b.t[0])*(a.t[0]-b.t[0])+(a.t[1]-b.t[1])*(a.t[1]-b.t[1]);}
    int _get(int k,node q)
    {
        int ret=0;
        if (q.t[0]<lft[k]) ret+=(q.t[0]-lft[k])*(q.t[0]-lft[k]);
        if (q.t[0]>rght[k]) ret+=(q.t[0]-rght[k])*(q.t[0]-rght[k]);
        if (q.t[1]<down[k]) ret+=(q.t[1]-down[k])*(q.t[1]-down[k]);
        if (q.t[1]>up[k]) ret+=(q.t[1]-up[k])*(q.t[1]-up[k]);
        return ret;
    }
    int ans,qid;
    void query(int k,int q)
    {
        if (!k) return;
        if (p[k].id!=q)
        {
            int re=dis(p[k],_p[q]);
            if (ans>re) qid=p[k].id,ans=re;
            else if (ans==re) qid=min(qid,p[k].id);
        }
        int ll=_get(lc[k],_p[q]),rr=_get(rc[k],_p[q]);
        if (ll<=rr)
        {
            if (ll<=ans) query(lc[k],q);
            if (rr<=ans) query(rc[k],q);
        } else
        {
            if (rr<=ans) query(rc[k],q);
            if (ll<=ans) query(lc[k],q);
        }
    }
    void solve()
    {
        int c;
        for (int i=1;i<=n;i++) _p[i]=p[i];
        rt=build(0,1,n);
        for (int i=1;i<=n;i++)
        {
            ans=inf;qid=0;
            query(rt,i);
            c=i;
            if (c>qid) swap(c,qid);
            mp[c][qid]+=v[i];
        }
        map<int,int>::iterator it;
        for (int i=1;i<=n;i++)
            for (it=mp[i].begin();it!=mp[i].end();it++)
                gh.add(st[i],st[it->first],it->second);
    }
}kd;
int _n,_m,x,y,z;
int Q,ta,tb,qa,qb;
int main()
{
    scanf("%d",&n);
    st[1]=1;
    for (int i=1;i<=n;i++)
    {
        scanf("%d%d",&p[i].t[0],&p[i].t[1]);
        p[i].id=i;
        scanf("%d%d%d",&v[i],&_n,&_m);
        st[i+1]=st[i]+_n;
        for (int j=1;j<=_m;j++)
        {
            scanf("%d%d%d",&x,&y,&z);
            D.add(st[i]+x-1,st[i]+y-1,z);
        }
    }
    kd.solve();
    for (int i=1;i<=n;i++)
        D.get_gh(i,st[i],st[i+1]-1);
    for (int i=1;i<=st[n+1]-1;i++)
        if (!gh.fa[i][0])
            gh.pre(i,i,0);
    scanf("%d",&Q);
    while (Q--)
    {
        scanf("%d%d%d%d",&ta,&tb,&qa,&qb);
        x=st[ta]+qa-1;y=st[tb]+qb-1;
        printf("%d\n",gh.asklca(x,y));
    }
}

原文地址:https://www.cnblogs.com/coco-night/p/9691008.html

时间: 2024-08-04 14:02:09

[2016北京集训试题6]网络战争-[最小割树(网络流)+kd-tree+倍增]的相关文章

【模板】最小割树(Gomory-Hu Tree)

传送门 Description 给定一个\(n\)个点\(m\)条边的无向连通图,多次询问两点之间的最小割 两点间的最小割是这样定义的:原图的每条边有一个割断它的代价,你需要用最小的代价使得这两个点不连通 Solution 对于一张无向图,如果 \(s \rightarrow t\) 的最大流是 \(f\),\(s\), \(t\) 所在的割集为 \(S\), \(T\),那么 \(\forall_{x \in S, y \in T}\), \(\operatorname{maxflow}(x

[2016北京集训试题14]股神小D-[LCT]

Description Solution 将(u,v,l,r)换为(1,u,v,l)和(2,u,v,r).进行排序(第4个数为第一关键字,第1个数为第二关键字).用LCT维护联通块的合并和断开.(维护联通块的大小,要维护虚边) 答案统计:每当四元组的第一个数为1(这时候合并点u,v所在连通块,反之拆开),在合并前ans+=size[u]*size[v]即可. Code #include<iostream> #include<cstdio> #include<cstring&g

[2016北京集训试题15]项链-[FFT]

Description Solution 设y[i+k]=y[i]+n. 由于我们要最优解,则假如将x[i]和y[σ[i]]连线的话,线是一定不会交叉的. 所以,$ans=\sum (x_{i}-y_{i+s}+c)^{2}$ 拆开得$ans=\sum (x_{i}^{2}+y_{i+s}^{2}+c^{2}-2x_{i}y_{i+s}+2x_{i}c-2y_{i+s}c)$ 其中,$x_{i}y_{i+s}$是卷积形式. 我们把经过处理的y数组reverse一下,和x数组进行卷积(这里用ntt

【2016北京集训测试赛(八)】 crash的数列

Description 题解 题目说这是一个具有神奇特性的数列!这句话是非常有用的因为我们发现,如果套着这个数列的定义再从原数列引出一个新数列,它居然还是一样的...... 于是我们就想到了能不能用多点数列套着来加速转移呢? 但是发现好像太多数列套起来是可以烦死人的...... 我们就采用嵌套两次吧,记原数列为A,第一层嵌套为B,第二层嵌套为C. 我们其实可以发现一些规律,对于Ci,它对应了B中i的个数:对于Bi,它对应了A中i的个数. 稍加处理即可,我们一边计算一边模拟数列的运算,同时可以计算

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

题面 思路 首先吐槽一下: 这题是什么东西啊??出题人啊,故意拼题很有意思吗??还拼两个这么毒瘤的东西???? 10K代码了解一下???? 然后是正经东西 首先,本题可以理解为这样: 给定$n$个块,每个块有一个根,每个根只会主动连出去一条无向边,每次求两点最小割 那么,我们显然可以把每个块内的最小割树建立出来,同时把块的根之间的最小割树也建立出来 如果询问点在同一个块里面,显然可以直接最小割树处理 否则就是两边的点到块根的最小割和两个块根之间的最小割的最小值 所以,我们先对于所有的块根,建出K

【BZOJ-2229】最小割 最小割树(最大流+分治)

2229: [Zjoi2011]最小割 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 1565  Solved: 560[Submit][Status][Discuss] Description 小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割. 对于带权图来说,将所有顶点处在不同部分的边的

【BZOJ-4435】Juice Junctions 最小割树(分治+最小割)+Hash

4435: [Cerc2015]Juice Junctions Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 20  Solved: 11[Submit][Status][Discuss] Description 你被雇佣升级一个旧果汁加工厂的橙汁运输系统.系统有管道和节点构成.每条管道都是双向的,且每条管道的流量都是1升每秒.管道可能连接节点,每个节点最多可以连接3条管道.节点的流量是无限的.节点用整数1到n来表示.在升级系统之前,你需要对现有

bzoj2229: [Zjoi2011]最小割(分治最小割+最小割树思想)

2229: [Zjoi2011]最小割 题目:传送门 题解: 一道非常好的题目啊!!! 蒟蒻的想法:暴力枚举点对跑最小割记录...绝对爆炸啊.... 开始怀疑是不是题目骗人...难道根本不用网络流???一看路牌....分治最小割?最小割树? 然后开始各种%论文... 简单来说吧,根据各种本蒟蒻不会证明的理论,那么:所有最小割都不是完全独立的,总共有n-1种(也就是树上的n-1条边)最小割 恰好和树的定义一样啊! 那么用一个solve递归函数来解决,一开始任意找两个点作为st和ed来最小割,然后分

bzoj2229: [Zjoi2011]最小割(最小割树)

传送门 这题是用最小割树做的(不明白最小割树是什么的可以去看看这一题->这里) 有了最小割树就很简单了……点数那么少……每次跑出一个最大流就暴力搞一遍就好了 1 //minamoto 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #include<algorithm> 6 #include<queue> 7 #define inf 0x3f3f3f3f 8 usi