题解:

好题!!

这题似乎能上我代码长度记录的前五?

调试时间长度应该也能上前五QAQ

首先题目要求的明显就是最小割,当然在整个森林上求Q次最小割肯定是会GG的,所以我们需要一个能快速求最小割的算法——最小割树。

最小割树,也叫分治最小割,就是通过预处理把原本的图缩成一颗树,树上两个节点路径上的最小边权就是它们的最小割,这个用树上倍增可以随便维护。

大概思想就是先求一次最小割,把划分出的S和T两个点集继续求最小割,向下分治然后连边缩点。

这题先对每个州预处理最小割树,州和州之间用KD树求出距离最近的点,然后查询的时候用树上倍增跳。

分别写出来就好了qwq

所以这就是道码农题,码码码码码码

有必要说一下时间复杂度是O(玄学),这个时间复杂度严格来说是过不了的,但是数据随机,每个州和大的森林联通块的期望大小不大,所以不知道为什么就过了。。。

PS:我没写KD树,写了个排序剪枝,也是玄学就过了。。。

代码:

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#define inf 2147483647
#define eps 1e-9
using namespace std;
typedef long long ll;
struct node{
int x,y,id;
friend bool operator <(node a,node b){
if(a.x!=b.x)return a.x<b.x;
if(a.y!=b.y)return a.y<b.y;
return a.id<b.id;
}
}p[],pp[];
int N,n,m,q,u,v,w,ta,tb,qa,qb,cnt=,bcc=,num[],fr[],pts[],blg[];
bool used[];
namespace capitals{
struct edge{
int v,w,next;
}a[];
int tot=,head[],dep[],fa[][],minn[][];
void add(int u,int v,int w){
a[++tot].v=v;
a[tot].w=w;
a[tot].next=head[u];
head[u]=tot;
}
void dfs(int u,int ff,int dpt,int d){
blg[u]=bcc;
dep[u]=dpt;
fa[u][]=ff;
minn[u][]=d;
for(int i=;i<=;i++){
fa[u][i]=fa[fa[u][i-]][i-];
minn[u][i]=min(minn[u][i-],minn[fa[u][i-]][i-]);
}
for(int tmp=head[u];tmp!=-;tmp=a[tmp].next){
int v=a[tmp].v;
if(v!=ff){
dfs(v,u,dpt+,a[tmp].w);
}
}
}
int query(int u,int v){
if(dep[u]<dep[v])swap(u,v);
int ret=inf;
for(int i=;i>=;i--){
if(dep[fa[u][i]]>=dep[v]){
ret=min(ret,minn[u][i]);
u=fa[u][i];
}
}
if(u==v)return ret;
for(int i=;i>=;i--){
if(fa[u][i]!=fa[v][i]){
ret=min(ret,min(minn[u][i],minn[v][i]));
u=fa[u][i],v=fa[v][i];
}
}
return min(ret,min(minn[u][],minn[v][]));
}
}
namespace dinic{
struct edge{
int v,w,next;
}a[];
int n,m,vs,vt,tot=,dep[],s[],flw[],nmd[],head[];
queue<int>q;
void add(int u,int v,int w){
a[++tot].v=v;
a[tot].w=w;
a[tot].next=head[u];
head[u]=tot;
}
void clr(){
//memset(head,-1,sizeof(head));
for(int i=;i<=n;i++)head[i]=-;
tot=;
}
bool bfs(){
for(int i=;i<=n;i++)dep[i]=;
while(!q.empty())q.pop();
q.push(vs);
dep[vs]=;
while(!q.empty()){
int u=q.front();
q.pop();
for(int tmp=head[u];tmp!=-;tmp=a[tmp].next){
int v=a[tmp].v;
if(!dep[v]&&a[tmp].w){
dep[v]=dep[u]+;
if(v==vt)return true;
q.push(v);
}
}
}
return false;
}
int dfs(int u,int num){
if(u==vt||!num)return num;
int ans=;
for(int tmp=head[u];tmp!=-;tmp=a[tmp].next){
int v=a[tmp].v;
if(dep[v]==dep[u]+&&a[tmp].w){
int f=dfs(v,min(num,a[tmp].w));
if(f){
a[tmp].w-=f;
a[tmp^].w+=f;
ans+=f;
num-=f;
if(!num)break;
}
}
}
if(!ans)dep[u]=-;
return ans;
}
void build_mincost(int l,int r){
if(r<=l)return;
int dnc=,ls=l,rs=r;
vs=nmd[l],vt=nmd[r];
for(int i=;i<=tot+;i++)a[i].w=flw[i/];
while(bfs())dnc+=dfs(vs,inf);
for(int i=l;i<=r;i++){
if(dep[nmd[i]])s[ls++]=nmd[i];
else s[rs--]=nmd[i];
}
for(int i=l;i<=r;i++)nmd[i]=s[i];
capitals::add(vs+cnt,vt+cnt,dnc);
capitals::add(vt+cnt,vs+cnt,dnc);
//printf("Added %d->%d Flow=%d\n",vs+cnt,vt+cnt,dnc);
build_mincost(l,ls-);
build_mincost(rs+,r);
}
}
int dis(node a,node b){
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
void solve(int k){
int ans=inf,now=;
for(int i=k+;i<=N;i++){
if((pp[i].x-pp[k].x)*(pp[i].x-pp[k].x)>ans)break;
int ds=dis(pp[i],pp[k]);
if(ds<ans){
ans=ds;
now=i;
}else if(ds==ans&&pp[i].id<pp[now].id)now=i;
}
for(int i=k-;i;i--){
if((pp[i].x-pp[k].x)*(pp[i].x-pp[k].x)>ans)break;
int ds=dis(pp[i],pp[k]);
if(ds<ans){
ans=ds;
now=i;
}else if(ds==ans&&pp[i].id<pp[now].id)now=i;;
}
fr[pp[k].id]=pp[now].id;
}
int main(){
memset(capitals::head,-,sizeof(capitals::head));
scanf("%d",&N);
for(int i=;i<=N;i++){
scanf("%d%d%d%d%d",&p[i].x,&p[i].y,&num[i],&n,&m);
p[i].id=i;
pp[i]=p[i];
pts[i]=cnt+;
dinic::n=n;
dinic::m=m;
dinic::clr();
for(int j=;j<=n;j++)dinic::nmd[j]=j;
for(int j=;j<=m;j++){
scanf("%d%d%d",&u,&v,&w);
dinic::add(u,v,w);
dinic::add(v,u,w);
dinic::flw[j]=w;
}
dinic::build_mincost(,n);
cnt+=n;
}
sort(pp+,pp+N+);
for(int i=;i<=N;i++)solve(i);
for(int i=;i<=N;i++){
if(used[i])continue;
if(i==fr[fr[i]]){
used[fr[i]]=true;
capitals::add(pts[i],pts[fr[i]],num[i]+num[fr[i]]);
capitals::add(pts[fr[i]],pts[i],num[i]+num[fr[i]]);
//printf("Added %d->%d Flow=%d\n",pts[fr[i]],pts[i],num[i]+num[fr[i]]);
}else{
capitals::add(pts[i],pts[fr[i]],num[i]);
capitals::add(pts[fr[i]],pts[i],num[i]);
//printf("Added %d->%d Flow=%d\n",pts[fr[i]],pts[i],num[i]);
}
}
for(int i=;i<=N;i++){
if(!blg[pts[i]]){
bcc++;
capitals::dfs(pts[i],,,inf);
}
}
scanf("%d",&q);
for(int i=;i<=q;i++){
scanf("%d%d%d%d",&ta,&tb,&qa,&qb);
qa=pts[ta]+qa-;
qb=pts[tb]+qb-;
if(blg[qa]!=blg[qb])puts("");
else printf("%d\n",capitals::query(qa,qb));
}
return ;
}

(2016北京集训十三)【xsy1532】网络战争 - 最小割树+树上倍增+KD树的更多相关文章

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

    Description A 联邦国有 N 个州,每个州内部都有一个网络系统,有若干条网络线路,连接各个 州内部的城市. 由于 A 国的州与州之间的关系不是太好,每个州都只有首府建立了到别的州的网络.具 ...

  2. (2016北京集训十三)【xsy1531】魔法游戏 - Nim游戏

    题解: 好题!我的结论很接近正解了... 把一个数化成二进制,每次至少要拿走一位,最多全拿走,不能不拿.那么这就是一个经典的Nim问题了,子树异或起来就是根节点的答案,随便递推一下就行了. 代码: # ...

  3. (2016北京集训十三)【xsy1533】mushroom - bitset

    题解: 神题...我看到的时候直接吓懵了... 这是一道STL题...否则可能要写可持久化ETT或者可持久化Toptree? 用bitset来维护每个蘑菇上哪里有杂草,那么 对于操作1和操作2:可以预 ...

  4. 2016北京集训测试赛(十三) Problem B: 网络战争

    Solution KD tree + 最小割树

  5. (2016北京集训十)【xsy1528】azelso - 概率期望dp

    北京集训的题都是好题啊~~(于是我爆0了) 注意到一个重要的性质就是期望是线性的,也就是说每一段的期望步数可以直接加起来,那么dp求出每一段的期望就行了... 设$f_i$表示从$i$出发不回到$i$ ...

  6. 2016北京集训测试赛(六)Problem B: 矩阵

    Solution 最小割. 参考BZOJ 3144切糕 在那道题的基础上将建图方法稍作变形: 我们对格子进行黑白染色, 对于两个格子之和\(\le k\)的限制, 就可以确定其中一个是白色格子, 一个 ...

  7. LOJ6045 雅礼集训 2017 Day8 价(最小割)

    由Hall定理,任意k种减肥药对应的药材数量>=k.考虑如何限制其恰好为k,可以将其看作是使对应的药材数量尽量少. 考虑最小割.建一个二分图,左边的点表示减肥药,右边的点表示药材.减肥药和其使用 ...

  8. [NOIP2013/Codevs3287]货车运输-最小[大]生成树-树上倍增

    Problem 树上倍增 题目大意 给出一个图,给出若干个点对u,v,求u,v的一条路径,该路径上最小的边权值最大. Solution 看到这个题第一反应是图论.. 然而,任意路径最小的边权值最大,如 ...

  9. 【2016北京集训】Mushroom

    Portal --> broken qwq Description 一开始有个蘑菇,蘑菇里面有\(n\)个房间,是一棵有根树,\(1\)号是根,每个房间里面都有杂草,现在要支持以下操作:将某个指 ...

随机推荐

  1. vue中插件的使用

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. GFM(GitHub Flavored Markdown)与标准Markdown的语法区别

    没有就自己造 其实这篇文16年底的时候就写好的,只不过当时是记在自己的笔记上(没错,我笔记大多记在本地,比发表的这寥寥几篇那是多的多的多了

  3. 第六章 Python之迭代器与生成器

    迭代器 迭代:迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果是下一次重复的初始值 l=['a','b','c'] count=0 while count < len(l): pri ...

  4. CF960F Pathwalks_权值线段树_LIS

    很不错的一道思维题. Code: #include<cstdio> #include<algorithm> #include<iostream> using nam ...

  5. 模板 NTT 快速数论变换

    NTT裸模板,没什么好解释的 这种高深算法其实也没那么必要知道原理 #include <cstdio> #include <cstring> #include <algo ...

  6. 《你又怎么了我错了行了吧》【Beta】Scrum Meeting 2

    第二天 日期:2019/6/25 前言: 第2次会议在女生宿舍召开 确认编码阶段已经完成,继续测试项目 1.1 今日完成任务情况以及明日任务安排 姓名 当前阶段任务 下一阶段任务 刘 佳 完善了未开发 ...

  7. angular-代码段

    重复代码 <div ng-app="" ng-init="names=['Jani','Hege','Kai']"> <p>使用 ng- ...

  8. MySQL高可用系列之MHA(二)

    一.參数说明 MHA提供了一系列配置參数.深入理解每一个參数的详细含义,对优化配置.合理使用MHA非常重要.非常多高可用性也都是通过合理配置一些參数而实现的. MHA包含例如以下配置參数,分别说明例如 ...

  9. UltraEdit正則表達式介绍及实例

    前几天,有个将Excel中的数据导入到数据库中的需求.原本想到用程序读取Excel中的数据并存储到数据库中,但经一哥们的提醒,说用 EditPlus或UltraEdit这种工具直接将数据拼凑成SQL插 ...

  10. 数据库之Case When

    近期几天的工作本来组长是安排我用mindfocion画几个图,本来以为难点是这个控件的使用,可是开发的时候才发现由于数据量有点多,所以在开发的时候汇总这些信息倒是费了我许多的功夫,最后总结一下就是写了 ...