LOJ 6068「2017 山东一轮集训 Day4」棋盘
题意
一个 \(n\times n\) 的棋盘上面有若干障碍物。
定义两个棋子可以互相攻击当且仅当这两个棋子的横坐标或纵坐标相等而且中间不能隔着障碍物。(可以隔棋子)
有 \(q\) 次询问,每次询问你要回答在棋盘上摆 \(x\) 枚棋子最少互相能攻击到的棋子对数。
\(\texttt{Data Range:}1\leq n\leq 50,1\leq q\leq 10^4\)
题解
我咋连套路都不会了啊……
考虑二分图,将每一行每一列被 # 隔开的小段缩成一个点,对于每个可以放棋子位置像所属横纵坐标的小段连边。
但是一小段内放了 \(x\) 个棋子会对答案贡献 \(\binom{x}{2}\),我们要考虑如何最小化这个东西。
按照蓝书上的套路,考虑将一条边拆成流量为 \(1\),费用为 \(1,2,\cdots,x\) 的多条边,然后跑单路增广并且记录答案就行了。
注意到流量的意义就是放棋子的枚数,所以不能按照平常的写法来。
代码
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
const ll MAXN=1e4+51,inf=0x7fffffff;
struct Edge{
ll to,prev,flow,cost;
};
Edge ed[MAXN<<1];
ll n,source,sink,tot=1,maxFlow,minCost,limit,qcnt,curx;
char ch[51][51];
ll last[MAXN],vis[MAXN],dist[MAXN],res[MAXN],back[MAXN];
ll idx[51][51],idy[51][51],sz[MAXN];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
inline void addEdge(ll from,ll to,ll flow,ll cost)
{
ed[++tot].prev=last[from];
ed[tot].to=to;
ed[tot].flow=flow;
ed[tot].cost=cost;
last[from]=tot;
}
inline ll Min(ll x,ll y)
{
return x<y?x:y;
}
inline bool spfa()
{
queue<ll>q;
ll top,to;
memset(vis,0,sizeof(vis));
memset(dist,0x3f,sizeof(dist));
memset(back,0,sizeof(back));
dist[source]=0,vis[source]=1,q.push(source);
while(!q.empty())
{
top=q.front();
q.pop(),vis[top]=0;
for(register int i=last[top];i;i=ed[i].prev)
{
to=ed[i].to;
if(dist[to]>dist[top]+ed[i].cost&&ed[i].flow)
{
dist[to]=dist[top]+ed[i].cost,back[ed[i].to]=i;
if(!vis[to])
{
q.push(to),vis[to]=1;
}
}
}
}
if(dist[sink]!=0x3f3f3f3f)
{
return 1;
}
return 0;
}
inline ll MCMF()
{
ll flow;
while(spfa()&&maxFlow<limit)
{
for(register int i=back[sink];i;i=back[ed[i^1].to])
{
ed[i].flow--,ed[i^1].flow++;
}
res[++maxFlow]=(minCost+=dist[sink]);
}
return maxFlow;
}
int main()
{
n=read();
for(register int i=1;i<=n;i++)
{
scanf("%s",ch[i]+1);
for(register int j=1;j<=n;j++)
{
if(ch[i][j]=='.')
{
limit++;
}
}
}
for(register int i=1;i<=n;i++)
{
for(register int j=1;j<=n;j++)
{
if(ch[i][j]!='#')
{
idx[i][j]=(ch[i][j]==ch[i][j-1]?idx[i][j-1]:++sink);
}
}
}
curx=sink;
for(register int i=1;i<=n;i++)
{
for(register int j=1;j<=n;j++)
{
if(ch[j][i]!='#')
{
idy[j][i]=(ch[j][i]==ch[j-1][i]?idy[j-1][i]:++sink);
}
}
}
for(register int i=1;i<=n;i++)
{
for(register int j=1;j<=n;j++)
{
if(ch[i][j]!='#')
{
sz[idx[i][j]]++,sz[idy[i][j]]++;
}
}
}
sink++;
for(register int i=1;i<=curx;i++)
{
for(register int j=0;j<sz[i];j++)
{
addEdge(source,i,1,j),addEdge(i,source,0,-j);
}
}
for(register int i=curx+1;i<sink;i++)
{
for(register int j=0;j<sz[i];j++)
{
addEdge(i,sink,1,j),addEdge(sink,i,0,-j);
}
}
for(register int i=1;i<=n;i++)
{
for(register int j=1;j<=n;j++)
{
if(ch[i][j]=='.')
{
addEdge(idx[i][j],idy[i][j],1,0);
addEdge(idy[i][j],idx[i][j],0,0);
}
}
}
MCMF(),qcnt=read();
for(register int i=0;i<qcnt;i++)
{
printf("%d\n",res[read()]);
}
}
LOJ 6068「2017 山东一轮集训 Day4」棋盘的更多相关文章
- Loj 6068. 「2017 山东一轮集训 Day4」棋盘
Loj 6068. 「2017 山东一轮集训 Day4」棋盘 题目描述 给定一个 $ n \times n $ 的棋盘,棋盘上每个位置要么为空要么为障碍.定义棋盘上两个位置 $ (x, y),(u, ...
- [LOJ#6068]. 「2017 山东一轮集训 Day4」棋盘[费用流]
题意 题目链接 分析 考虑每个棋子对对应的横向纵向的极大区间的影响:记之前这个区间中的点数为 \(x\) ,那么此次多配对的数量即 \(x\) . 考虑费用流,\(S\rightarrow 横向区间 ...
- Loj #6069. 「2017 山东一轮集训 Day4」塔
Loj #6069. 「2017 山东一轮集训 Day4」塔 题目描述 现在有一条 $ [1, l] $ 的数轴,要在上面造 $ n $ 座塔,每座塔的坐标要两两不同,且为整点. 塔有编号,且每座塔都 ...
- loj6068. 「2017 山东一轮集训 Day4」棋盘 二分图,网络流
loj6068. 「2017 山东一轮集训 Day4」棋盘 链接 https://loj.ac/problem/6068 思路 上来没头绪,后来套算法,套了个网络流 经典二分图 左边横,右边列 先重新 ...
- 「2017 山东一轮集训 Day4」棋盘(费用流)
棋盘模型 + 动态加边 #include<cstdio> #include<algorithm> #include<iostream> #include<cs ...
- Loj #6073.「2017 山东一轮集训 Day5」距离
Loj #6073.「2017 山东一轮集训 Day5」距离 Description 给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\) ...
- LOJ #6074. 「2017 山东一轮集训 Day6」子序列
#6074. 「2017 山东一轮集训 Day6」子序列 链接 分析: 首先设f[i][j]为到第i个点,结尾字符是j的方案数,这个j一定是从i往前走,第一个出现的j,因为这个j可以代替掉前面所有j. ...
- loj #6077. 「2017 山东一轮集训 Day7」逆序对
#6077. 「2017 山东一轮集训 Day7」逆序对 题目描述 给定 n,k n, kn,k,请求出长度为 n nn 的逆序对数恰好为 k kk 的排列的个数.答案对 109+7 10 ^ 9 ...
- LOJ #6119. 「2017 山东二轮集训 Day7」国王
Description 在某个神奇的大陆上,有一个国家,这片大陆的所有城市间的道路网可以看做是一棵树,每个城市要么是工业城市,要么是农业城市,这个国家的人认为一条路径是 exciting 的,当且仅当 ...
随机推荐
- 目标检测:SSD算法详解
一些概念 True Predict True postive False postive 预测为正类 False negivate True negivate 预测为负类 真实为 ...
- 神作!阿里首发并发编程神仙笔记,Github已星标92K
Qzone 微信 神作!阿里首发并发编程神仙笔记,Github已星标92K JAVA超神编程 2020-09-23 21:04:06 JVM 无论什么级别的Java从业者,JVM都是进阶时必须迈过的坎 ...
- 聊聊经典数据结构HashMap,逐行分析每一个关键点
本文基于JDK-8u261源码分析 本文原创首发于 奇客时间(qiketime) 1 简介 HashMap是一个使用非常频繁的键值对形式的工具类,其使用起来十分方便.但是需要注意的是,HashMap不 ...
- 遍历 Dictionary,你会几种方式?
一:背景 1. 讲故事 昨天在 StackOverflow 上看到一个很有趣的问题,说: 你会几种遍历字典的方式,然后跟帖就是各种奇葩的回答,挺有意思,马上就要国庆了,娱乐娱乐吧,说说这种挺无聊的问题 ...
- C#Messenger分析和使用方法
目录 源码分析 使用方法 没有返回值没有传参的消息写法 没有返回值有参数的消息写法 有返回值的消息写法 有参数有返回值的委托 C#Messenger是UntiyCommunity里的一个工具类,其效果 ...
- python 系统设置
1. 设置python运行环境为utf-8 import sys #引用sys模块 reload(sys) #重新加载sys sys.setdefaultencoding("utf-8&qu ...
- GAN生成的评价指标 Evaluation of GAN
传统方法中,如何衡量一个generator ?-- 用 generator 产生数据的 likelihood,越大越好. 但是 GAN 中的 generator 是隐式建模,所以只能从 P_G 中采样 ...
- thinkphp5 chunk 分块处理数据小坑
场景: 使用chunk方法进行分块查询写入数据,执行发现chunk分几条一次处理 数据库就插入几条,并没有return false; 代码如下 解决方法: 增加排序字段
- java的各种集合为什么不安全(List、Set、Map)以及代替方案
我们已经知道多线程下会有各种不安全的问题,都知道并发的基本解决方案,这里对出现错误的情况进行一个实际模拟,以此能够联想到具体的生产环境中. 一.List 的不安全 1.1 问题 看一段代码: publ ...
- Linux关联文件扩展名和打开程序
手动为Linux关联文件扩展名与打开程序. 当Linux不认识某种扩展名的时候,需要为此扩展名创建一个MIME类型.Linux是依据MIME类型而不是扩展名来决定打开程序的. 再将此种MIME类型与某 ...