题意

一个 \(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」棋盘的更多相关文章

  1. Loj 6068. 「2017 山东一轮集训 Day4」棋盘

    Loj 6068. 「2017 山东一轮集训 Day4」棋盘 题目描述 给定一个 $ n \times n $ 的棋盘,棋盘上每个位置要么为空要么为障碍.定义棋盘上两个位置 $ (x, y),(u, ...

  2. [LOJ#6068]. 「2017 山东一轮集训 Day4」棋盘[费用流]

    题意 题目链接 分析 考虑每个棋子对对应的横向纵向的极大区间的影响:记之前这个区间中的点数为 \(x\) ,那么此次多配对的数量即 \(x\) . 考虑费用流,\(S\rightarrow 横向区间 ...

  3. Loj #6069. 「2017 山东一轮集训 Day4」塔

    Loj #6069. 「2017 山东一轮集训 Day4」塔 题目描述 现在有一条 $ [1, l] $ 的数轴,要在上面造 $ n $ 座塔,每座塔的坐标要两两不同,且为整点. 塔有编号,且每座塔都 ...

  4. loj6068. 「2017 山东一轮集训 Day4」棋盘 二分图,网络流

    loj6068. 「2017 山东一轮集训 Day4」棋盘 链接 https://loj.ac/problem/6068 思路 上来没头绪,后来套算法,套了个网络流 经典二分图 左边横,右边列 先重新 ...

  5. 「2017 山东一轮集训 Day4」棋盘(费用流)

    棋盘模型 + 动态加边 #include<cstdio> #include<algorithm> #include<iostream> #include<cs ...

  6. Loj #6073.「2017 山东一轮集训 Day5」距离

    Loj #6073.「2017 山东一轮集训 Day5」距离 Description 给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\) ...

  7. LOJ #6074. 「2017 山东一轮集训 Day6」子序列

    #6074. 「2017 山东一轮集训 Day6」子序列 链接 分析: 首先设f[i][j]为到第i个点,结尾字符是j的方案数,这个j一定是从i往前走,第一个出现的j,因为这个j可以代替掉前面所有j. ...

  8. loj #6077. 「2017 山东一轮集训 Day7」逆序对

    #6077. 「2017 山东一轮集训 Day7」逆序对   题目描述 给定 n,k n, kn,k,请求出长度为 n nn 的逆序对数恰好为 k kk 的排列的个数.答案对 109+7 10 ^ 9 ...

  9. LOJ #6119. 「2017 山东二轮集训 Day7」国王

    Description 在某个神奇的大陆上,有一个国家,这片大陆的所有城市间的道路网可以看做是一棵树,每个城市要么是工业城市,要么是农业城市,这个国家的人认为一条路径是 exciting 的,当且仅当 ...

随机推荐

  1. vscode写latex的正反向搜索问题

    vscode写latex的正反向搜索问题 vscode使用LaTeX Workshop + TexLive时可以很舒畅的写作 但是在长篇写作时,会涉及到正反向搜索的需求.即根据源码搜pdf的位置,和根 ...

  2. 放弃"指针常量"这种不严谨的中文描述!深度理解数组名、指针常量

    ques1: 数组名完全等价于指针常量吗? int array[10] = { 10,11,12,13,14,15 }; printf("sizeof(array)= %d \n" ...

  3. 《C++primerplus》第11章练习题

    1.修改程序清单11.5(随机漫步),使之以特定的格式将结果写入文件中. //vector.h -- Vector Class #ifndef _VECTOR_H_ #define _VECTOR_H ...

  4. 【学习笔记/题解】树上启发式合并/CF600E Lomsat gelral

    题目戳我 \(\text{Solution:}\) 树上启发式合并,是对普通暴力的一种优化. 考虑本题,最暴力的做法显然是暴力统计每一次的子树,为了避免其他子树影响,每次统计完子树都需要清空其信息. ...

  5. 【题解】CF1228D Complete Tripartite

    Link 题目大意:给定一个无向图,将它划分为三个点集,要求在一个点集中的点没有边相连,且颜色相同,不同集合中的点互相有边相连. \(\text{Solution:}\) 我们发现,与一个点之间没有边 ...

  6. JavaScript innerTHML和createElement效率对比

    前言: 在DOM节点操作中,innerTHML和createElement都可以实现创建元素.它们实现的功能类似,但是效率却相差很大.本文分别统计用innerTHML字符串拼接方式.innerTHML ...

  7. DX12龙书 00 - 环境配置:通过 Visual Studio 2019 运行示例项目

    0x00 安装 Visual Studio 2019 安装 Visual Studio 2019 以及相关组件. 注:安装组件时带的 Windows 10 SDK 可以在 Individual com ...

  8. Git本地已有项目关联远程仓库

    情况: 本地已有项目 远程有个仓库 目的: 本地项目关联远程仓库 首先要把本地项目变成git管理的,也就是建立一个本地仓库,可以在项目目录下面使用git init命令初始化仓库,初始化成功之后会在仓库 ...

  9. MeteoInfoLab脚本示例:站点填图

    打开包含站点填图的站点数据文件(比如micaps 1)之后,用文件对象的smodeldata函数获取StationModel数据对象,然后用stationmodel函数绘制站点填图图层.脚本程序: # ...

  10. day60 Pyhton 框架Django 03

    day61 内容回顾 1.安装 1. 命令行: pip install django==1.11.18 pip install django==1.11.18 -i 源 2. pycharm sett ...