列表

详情


NC231404. [NOIP2021]棋局(chess)

描述

    在输了一晚上的麻将之后,小 z 和小 c 卸掉了手机上的所有牌类游戏。不过这怎么可能阻挡得了他们上课颓废的决心呢?现在他们的目光盯在了棋类游戏上,但他们两个   除了天天下飞行器以外,几乎所有棋类游戏都只懂个大概规则。

    “既然我们都会玩但只能玩一点点,不如我们自己搞个缝合怪出来吧!

    于是,在他们的精心脑洞之下,一个融合了围棋、象棋与军棋的奇妙游戏诞生了……

    游戏在一张长 n 行宽 m 列的网格形棋盘上进行,棋子落在网格的交叉点上。我们不妨记左上角的交叉点的坐标为 (1,1) ,右下角的交叉点坐标为 (n, m)

    棋子分为黑白两色,对局双方各执一方棋子。

    每个棋子除了颜色以外还有等级,不妨设 col_i  为棋子 i 的颜色,lv_i  为棋子i的等级。另外,棋盘上的网格线共有 4 种状态,对于第 i 条网格线,设其状态为 opt_i 。

    轮到每方下棋时,他可以选择棋盘上的一个己方棋子沿网格线进行移动到另一个交叉点,称为走子。形式化定义走子的过程如下:选择一个坐标序列 (x_0,y_0),(x_1,y_1), ...,(x_k,y_k),其中 k 是任意选定的正整数,(x_0, y_0) 是棋子初始的位置,(x_k, y_k) 是棋子最终走到的位置,需要满足:

    •    对于任意  ,坐标 (x_i,y_i) 和  之间必须有网格线直接相连,也就是说走子必须沿着网格线走;

    •    对于任意  ,必须有  ,也就是说走子过程中不能经过重复位置,特别地 ,也就是说不能原地不动(或走回原地)。

    •    对于任意 ,坐标 (x_i,y_i) 上必须没有棋子,也就是说走子时不能越过已有的棋子。

    •    若 (x_k,y_k) 上没有棋子,称为普通走子,否则称为吃子。在吃子过程中,设正在走的棋子颜色为 col_1 ,等级为 lv_1 ,被吃的棋子颜色为 col_2 ,等级为 lv_2 ,则必须满足  ,换句话说只能吃与自己颜色不同,且等级不高于自己等级的棋子。
    需要注意的是,由上述定义可以得出,不允许棋子在吃子后继续向前走。  

    网格线的状态含义如下所述:

    •    如果 ,代表此路不通,走子时不能经过这条网格线。

    •    如果 ,代表这条网格线是一条普通道路,每次走子时棋子最多只能经过 $1$ 条普通道路。

    •    如果 ,代表这条网格线是一条直行道路,每次走子时棋子可以经过任意条直行道路,但只能一直沿横向或一直沿纵向走,不能转弯。如沿直行道路从 (1,1) 经过 (1,2) 走到 (1,3) 是可以的,但是从 (1,1) 经过 (1,2) 走到 (2,2) 不行。

    •    如果 ,代表这条网格线是一条互通道路,每次走子时棋子可以经过任意条互通道路,且中途可任意转弯。

    同时规定在一次走子过程中,棋子经过的网格线的状态必须全部相同,比如从 (1,1) 经过直行道路走到 (1,2) 再经过互通道路走到 (1,3) 是不允许的。

    至于如何判断胜负等其他细节,与本题无关,故略去。

    小 z 和小 c 开发出这款棋类游戏后,为了提升水平,想了一个训练的策略:一开始棋盘是空的,然后小 c 会每次往棋盘的某个空交叉点上放一枚棋子,小 z 需要快速计算出:若选择这枚新放上的棋子进行一次走子,棋盘上一共有多少个位置是能被走到的?   注意,因为这只是思维训练,他们并不会真的走这枚棋子。

    可怜的小 z 发现他的计算力不足以算出这个问题,只好向你求助。

输入描述

第一行:一个正整数 T 表示数据组数。对于每组数据:

第一行:3 个正整数 n, m, q,分别表示棋盘的行数、列数和游戏的轮数。

接下来 n 行,每行为一个长  的字符串,每个字符为  中的一个, 第i行第j个字符表示交叉点 (i, j) 连向交叉点  的网格线状态。

接下来  行,每行为一个长 m 的字符串,每个字符为  中的一个, 第i行第j个字符表示交叉点 (i, j) 连向交叉点  的网格线状态。

接下来 q 行,每行 4 个非负整数 col_i, lv_i, x_i, y_i,表示在第 i 轮有一枚颜色为 col_i , 等级为 lv_i 的棋子放在了交叉点  (x_i,y_i)  上。其中  表示黑子, 表示白子。保证之前交叉点 (x_i,y_i) 上没有棋子。

输出描述

对于每组数据输出 q 行,每行一个非负整数,表示第 i 枚棋子放置后能走到的交叉点数量。

示例1

输入:

1
3 3 5
13
22
23
010
233
0 1 2 3
1 2 2 1
1 3 1 2
0 2 3 2
1 3 2 2

输出:

4
3
3
3
2

说明:

放置棋子 1 后,它能走到的位置为 (2,1),(2,2),(3,2),(3,3)

放置棋子 2 后,它能走到的位置为 (2,2),(2,3),(3,1)

放置棋子 3 后,它能走到的位置为 (1,1),(1,3),(2,2)

放置棋子 4 后,它能走到的位置为 (2,2),(3,1),(3,3)

放置棋子 5 后,它能走到的位置为 (2,3),(3,2)

示例2

输入:

2
2 3 4
22
33
123
0 2 1 2
0 1 2 1
1 2 1 3
0 3 2 2
3 2 3
3
1
3
32
32
0 2 1 2
1 2 3 2
0 1 2 2

输出:

3
4
4
2
5
5
1

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(clang++ 11.0.1) 解法, 执行用时: 3631ms, 内存消耗: 215088K, 提交时间: 2022-08-23 20:26:02

#include<bits/stdc++.h>
using namespace std;
const int N=5e5;
#define x first
#define y second
#define pii pair<int,int>
int n,m,q,idlim;
int idh(int x,int y){ return x*(m+2)+y; }
pii hdi(int id){ return pii(id/(m+2),id%(m+2)); }
int idv(int x,int y){ return y*(n+2)+x; }
pii vdi(int id){ return pii(id%(n+2),id/(n+2)); }
template<class T>
struct array2d{
	T a[N];
	int n,m;
	void init(int val=0){
		memset(a,val,sizeof(a));
	}
	void set_size(int n,int m,int val=0){
		this->n=n;
		this->m=m;
		init(val);
	}
	T* operator[](int i){
		return a+i*(m+2);
	}
	const T* operator[](int i)const{
		return a+i*(m+2);
	}
	T& operator[](pii p){
		return a[p.x*(m+2)+p.y];
	}
	const T& operator[](pii p)const{
		return a[p.x*(m+2)+p.y];
	}
};
array2d<int> v,h,col,lv,tim;
vector<pii> piece;
 
struct node{
	int ch[2],sz;
};
node t[N*30];int cnt;
void pushup(int x){
	t[x].sz=t[t[x].ch[0]].sz+t[t[x].ch[1]].sz;
}
void insert(int p,int &x,int l,int r){
	if(!x)x=++cnt;
	if(l==r){ t[x].sz=1;return; }
	int mid=(l+r)>>1;
	if(p<=mid)insert(p,t[x].ch[0],l,mid);
	else insert(p,t[x].ch[1],mid+1,r);
	pushup(x);
}
void erase(int p,int &x,int l,int r){
	if(!x)return;
	if(l==r){ t[x].sz=0;return; }
	int mid=(l+r)>>1;
	if(p<=mid)erase(p,t[x].ch[0],l,mid);
	else erase(p,t[x].ch[1],mid+1,r);
	pushup(x);
}
int merge(int x,int y){
	if(!(x&&y))return x+y;
	t[x].ch[0]=merge(t[x].ch[0],t[y].ch[0]);
	t[x].ch[1]=merge(t[x].ch[1],t[y].ch[1]);
	if(t[x].ch[0]||t[x].ch[1])pushup(x);
	else t[x].sz|=t[y].sz;
	return x;
}
int query_rk(int v,int x,int l,int r){
	if(!t[x].sz)return 0;
	if(l==r)return t[x].sz;
	int mid=(l+r)>>1;
	if(v<=mid)return query_rk(v,t[x].ch[0],l,mid);
	else return t[t[x].ch[0]].sz+query_rk(v,t[x].ch[1],mid+1,r);
}
bool exist(int v,int x,int l,int r){
	if(!t[x].sz)return 0;
	if(l==r)return 1;
	int mid=(l+r)>>1;
	if(v<=mid)return exist(v,t[x].ch[0],l,mid);
	else return exist(v,t[x].ch[1],mid+1,r);
}
 
struct DSU_with_lr{
	int l[N],r[N],f[N];
	int _(int x){ return f[x]==x?x:f[x]=_(f[x]); }
	void merge(int x,int y){
		x=_(x),y=_(y);
		if(x==y)return;
		f[x]=y;
		l[y]=min(l[y],l[x]);
		r[y]=max(r[y],r[x]);
	}
	int getl(int id){ return l[_(id)]; }
	int getr(int id){ return r[_(id)]; }
	bool check(int x,int y){ return _(x)==_(y); }
};
DSU_with_lr hseg,vseg;
 
struct block{
	int rt0,rt1,rth,rtv;
	void merge(block &y){
		rt0=::merge(rt0,y.rt0);
		rt1=::merge(rt1,y.rt1);
		rth=::merge(rth,y.rth);
		rtv=::merge(rtv,y.rtv);
	}
	int get0(int v){ return query_rk(v,rt0,1,q); }
	void ins0(int v){ insert(v,rt0,1,q); }
	void era0(int v){ erase(v,rt0,1,q); }
	int get1(int v){ return query_rk(v,rt1,1,q); }
	void ins1(int v){ insert(v,rt1,1,q); }
	void era1(int v){ erase(v,rt1,1,q); }
	void ins01(int col,int v){ col?ins1(v):ins0(v); }
	void era01(int col,int v){ col?era1(v):era0(v); }
	void insp(int x,int y){
		insert(idh(x,y),rth,1,idlim);
		insert(idv(x,y),rtv,1,idlim);
	}
	int getsz(int col,int lv){
		return t[rth].sz+(1-col?get1(lv):get0(lv));
	}
	int geth(int x,int y1,int y2,int colx,int lvx){
		auto check=[&](int p,int q){
			return col[p][q]==1-colx&&lv[p][q]<=lvx&&exist(lv[p][q],1-colx?rt1:rt0,1,::q);
		};
		return query_rk(idh(x,y2),rth,1,idlim)-query_rk(idh(x,y1-1),rth,1,idlim)
			  +(h[x][y1-1]==2&&check(x,y1-1))+(h[x][y2]==2&&check(x,y2+1));
	}
	int getv(int x1,int x2,int y,int colx,int lvx){
		auto check=[&](int p,int q){
			return col[p][q]==1-colx&&lv[p][q]<=lvx&&exist(lv[p][q],1-colx?rt1:rt0,1,::q);
		};
		return query_rk(idv(x2,y),rtv,1,idlim)-query_rk(idv(x1-1,y),rtv,1,idlim)
			  +(v[x1-1][y]==2&&check(x1-1,y))+(v[x2][y]==2&&check(x2+1,y));
	}
};
template<class T>
struct DSU_array2d{
	array2d<T>a;
	array2d<pii>f;
	void init(int val=0){
		a.init(val);
	}
	void set_size(int n,int m,int val=0){
		a.set_size(n,m,val);
		f.set_size(n,m,val);
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				f[i][j]={i,j};
	}
	pii _(pii x){ return x==f[x]?x:f[x]=_(f[x]); }
	T& operator[](pii p){
		return a[_(p)];
	}
	void merge(pii x,pii y){
		x=_(x),y=_(y);
		if(x==y)return;
		a[x].merge(a[y]);
		f[y]=x;
	}
};
DSU_array2d<block> b;
 
int qsum=0;
int ans[N];
int lvcnt[N];
void mian(){
	cin>>n>>m>>q;
	v.set_size(n,m);
	h.set_size(n,m);
	col.set_size(n,m,-1);
	lv.set_size(n,m);
	tim.set_size(n,m);
	piece.clear();
	memset(t,0,sizeof(t));
	memset(&hseg,0,sizeof(hseg));
	memset(&vseg,0,sizeof(vseg));
	memset(lvcnt,0,sizeof(lvcnt));
	b.set_size(n,m);
	for(int i=0;i<N;i++)hseg.l[i]=hseg.r[i]=hseg.f[i]=i;
	for(int i=0;i<N;i++)vseg.l[i]=vseg.r[i]=vseg.f[i]=i;
	idlim=(n+3)*(m+3);
 
	cnt=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<m;j++){
			char c;cin>>c;
			h[i][j]=c-'0';
		}
	for(int i=1;i<n;i++)
		for(int j=1;j<=m;j++){
			char c;cin>>c;
			v[i][j]=c-'0';
		}
	for(int i=1;i<=q;i++){
		int colx,lvx,x,y;
		cin>>colx>>lvx>>x>>y;
		col[x][y]=colx;
		lv[x][y]=lvx;
		++lvcnt[lvx];
		tim[x][y]=i;
		piece.push_back(pii(x,y));
	}
	for(int i=1;i<=q;i++)lvcnt[i]+=lvcnt[i-1];
	for(int i=q-1;i>=0;--i)lv[piece[i].x][piece[i].y]=lvcnt[lv[piece[i].x][piece[i].y]]--;
	for(int i=1;i<=n;i++){
		for(int j=1;j<m;j++){
			if(h[i][j]==2&&!lv[i][j]&&!lv[i][j+1])hseg.merge(idh(i,j),idh(i,j+1));
			if(h[i][j]==3&&!lv[i][j]&&!lv[i][j+1])b.merge({i,j},{i,j+1});
			if(h[i][j]==3&&lv[i][j]&&!lv[i][j+1]){
				b[{i,j+1}].ins01(col[i][j],lv[i][j]);
			}
			if(h[i][j]==3&&!lv[i][j]&&lv[i][j+1]){
				b[{i,j}].ins01(col[i][j+1],lv[i][j+1]);
			}
		}
	}
	for(int i=1;i<n;i++){
		for(int j=1;j<=m;j++){
			if(v[i][j]==2&&!lv[i][j]&&!lv[i+1][j])vseg.merge(idv(i,j),idv(i+1,j));
			if(v[i][j]==3&&!lv[i][j]&&!lv[i+1][j])b.merge({i,j},{i+1,j});
			if(v[i][j]==3&&lv[i][j]&&!lv[i+1][j]){
				b[{i+1,j}].ins01(col[i][j],lv[i][j]);
			}
			if(v[i][j]==3&&!lv[i][j]&&lv[i+1][j]){
				b[{i,j}].ins01(col[i+1][j],lv[i+1][j]);
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(!lv[i][j]){
				b[{i,j}].insp(i,j);
			}
		}
	}
	for(int i=q-1;i>=0;--i){
		int ans=-1;
		int x=piece[i].x,y=piece[i].y;
		int lvx=lv[x][y],colx=col[x][y];
		lv[x][y]=0; col[x][y]=-1;
 
		if(h[x][y]==2&&!lv[x][y]&&!lv[x][y+1])hseg.merge(idh(x,y),idh(x,y+1));
		if(h[x][y-1]==2&&!lv[x][y-1]&&!lv[x][y])hseg.merge(idh(x,y-1),idh(x,y));
		pii l=hdi(hseg.getl(idh(x,y))),r=hdi(hseg.getr(idh(x,y)));
		ans+=r.y-l.y+1;
 
		if(v[x][y]==2&&!lv[x][y]&&!lv[x+1][y])vseg.merge(idv(x,y),idv(x+1,y));
		if(v[x-1][y]==2&&!lv[x-1][y]&&!lv[x][y])vseg.merge(idv(x-1,y),idv(x,y));
		pii u=vdi(vseg.getl(idv(x,y))),d=vdi(vseg.getr(idv(x,y)));
		ans+=d.x-u.x+1;
 
		b[{x,y}].insp(x,y);
		if(h[x][y]==3){
			if(!lv[x][y+1])b.merge({x,y},{x,y+1});
			else b[{x,y}].ins01(col[x][y+1],lv[x][y+1]);
		}
		if(h[x][y-1]==3){
			if(!lv[x][y-1])b.merge({x,y-1},{x,y});
			else b[{x,y}].ins01(col[x][y-1],lv[x][y-1]);
		}
		if(v[x][y]==3){
			if(!lv[x+1][y])b.merge({x,y},{x+1,y});
			else b[{x,y}].ins01(col[x+1][y],lv[x+1][y]);
		}
		if(v[x-1][y]==3){
			if(!lv[x-1][y])b.merge({x-1,y},{x,y});
			else b[{x,y}].ins01(col[x-1][y],lv[x-1][y]);
		}
		b[{x,y}].era01(colx,lvx);
		ans+=b[{x,y}].getsz(colx,lvx);
		ans-=b[{x,y}].geth(l.x,l.y,r.y,colx,lvx);
		ans-=b[{x,y}].getv(u.x,d.x,u.y,colx,lvx);
 
		if(col[l.x][l.y-1]==1-colx&&h[l.x][l.y-1]==2&&lv[l.x][l.y-1]<=lvx)++ans,--l.y;
		if(col[r.x][r.y+1]==1-colx&&h[r.x][r.y  ]==2&&lv[r.x][r.y+1]<=lvx)++ans,++r.y;
		if(col[u.x-1][u.y]==1-colx&&v[u.x-1][u.y]==2&&lv[u.x-1][u.y]<=lvx)++ans,--u.x;
		if(col[d.x+1][d.y]==1-colx&&v[d.x  ][d.y]==2&&lv[d.x+1][d.y]<=lvx)++ans,++d.x;
		
		auto check=[&](int p,int q){
			return !lv[p][q]||(col[p][q]==1-colx&&lv[p][q]<=lvx);
		};
		auto vis=[&](int p,int q){
			if(b._({p,q})==b._({x,y}))return 1;
			if(lv[p][q]&&exist(lv[p][q],col[p][q]?b[{x,y}].rt1:b[{x,y}].rt0,1,::q))return 1;
			return 0;
		};
		if(h[x][y]==1&&check(x,y+1)&&!vis(x,y+1))++ans;
		if(v[x][y]==1&&check(x+1,y)&&!vis(x+1,y))++ans;
		if(h[x][y-1]==1&&check(x,y-1)&&!vis(x,y-1))++ans;
		if(v[x-1][y]==1&&check(x-1,y)&&!vis(x-1,y))++ans;
		::ans[i]=ans;
	}
	for(int i=0;i<q;i++)cout<<ans[i]<<"\n";
	qsum+=q;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	int T;cin>>T;
	while(T--){
		mian();
	}
}

上一题