传送门
【题目分析】
首先我们将整个棋盘按平常的样子染为黑白两色。
然后就会发现,骑士跳跃方式是走到一个异色的点。
所以整个棋盘就被划分为一个二分图,一半为黑色一半为白色。
很明显每个白点向可以跳到的黑点连边,然后跑个最小割,最后用可以放的地方-最小割即可。
至于连边,因为数据规模不大,直接暴力连就行了。
【代码~】
#include<bits/stdc++.h>
using namespace std;
const int MAXP=2e2+10;
const int MAXN=5e5+10;
const int MAXM=1e6+10;
const int INF=0x3f3f3f3f;
int n,m,cnt,s,t;
int za[MAXP][MAXP];
int dx[8]={-2,-2,-1,-1,1,1,2,2};
int dy[8]={-1,1,-2,2,-2,2,-1,1};
int head[MAXN],cur[MAXN],depth[MAXN];
int nxt[MAXM],to[MAXM],w[MAXM];
int Read(){
int i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
void Add(int x,int y,int z){
nxt[cnt]=head[x];
head[x]=cnt;
to[cnt]=y;
w[cnt]=z;
cnt++;
}
void add(int x,int y,int z){
Add(x,y,z);
Add(y,x,0);
}
int bfs(){
queue<int> q;
memset(depth,0,sizeof(depth));
depth[s]=1;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=nxt[i]){
int v=to[i];
if(w[i]&&!depth[v]){
depth[v]=depth[u]+1;
q.push(v);
}
}
}
if(depth[t]==0)
return 0;
return 1;
}
int dfs(int u,int flow){
if(u==t)
return flow;
for(int &i=cur[u];i!=-1;i=nxt[i]){
int v=to[i];
if(depth[v]==depth[u]+1&&w[i]){
int di=dfs(v,min(w[i],flow));
if(di){
w[i]-=di;
w[i^1]+=di;
return di;
}
}
}
return 0;
}
int dinic(){
int ret=0;
while(bfs()){
for(int i=s;i<=t;++i)
cur[i]=head[i];
while(int d=dfs(s,INF))
ret+=d;
}
return ret;
}
int id(int x,int y){
return (x-1)*n+y;
}
int main(){
memset(head,-1,sizeof(head));
n=Read(),m=Read();
s=0,t=n*n+1;
for(int i=1;i<=m;++i){
int x=Read(),y=Read();
za[x][y]=1;
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if(!za[i][j]){
int x=id(i,j);
if(!((i+j)&1))
add(s,x,1);
else
add(x,t,1);
}
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if(!za[i][j]&&!((i+j)&1)){
for(int k=0;k<8;++k){
int nx=i+dx[k],ny=j+dy[k];
if(nx<1||nx>n||ny<1||ny>n||za[nx][ny])
continue;
int x=id(i,j),y=id(nx,ny);
add(x,y,INF);
}
}
}
}
cout<<n*n-m-dinic();
return 0;
}