chess草稿(附代码!)

2022/8/12日过了,代码如下:(已删除调试语句,保留注释,为了使代码更容易看懂并没有卡常。卡完常的代码不是给人看的)

点击查看代码
/*
倒序操作+合并连通块+维护集合,支持合并、区间查询+线段树合并
*/
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e6+50;
void InOut(int op)
{
	freopen("chess.in","r",stdin);
	if(op)
	freopen("chess.out","w",stdout);
}
int N,M,Q;
bool Can(int x,int y)
{
	if(x<1||y<1||x>N||y>M)
	return false;
	return true;
}
int ALL;//N*M
int Edge[4][MAXN<<1];
int Ans[MAXN];
struct Node
{
	int Color,Level,x,y,Id;
}Piece[MAXN];
bool CanEat(Node a,Node b)//a CanEat b
{
	if(a.Color!=b.Color&&a.Level>=b.Level)
	return true;
	return false;
}
bool cmp1(Node a,Node b)
{
	if(a.Level==b.Level)
	return a.Id<b.Id;
	return a.Level<b.Level;
}
bool cmp2(Node a,Node b)
{
	return a.Id<b.Id;
}
void LSHLevel()
{
	sort(Piece+1,Piece+Q+1,cmp1);
	for(int i=1;i<=Q;i++)
	{
		Piece[i].Level=i;
	}
	sort(Piece+1,Piece+Q+1,cmp2);
}
int HaveNode[MAXN];
int GetId(int op,int x,int y)//0:从上到下一行一行 :左右连续 		1:从左到右一列一列 ,上下连续 
{
	if(op==0)
	return (x-1)*M+y;
	else
	return (y-1)*N+x;
}
int GetX(int Id)
{
	return (Id-1)/M+1;
}
int GetY(int Id)
{
	return (Id-1)%M+1;
}
struct SegmentTree
{
	int Root[MAXN];
	int ls[MAXN<<2],rs[MAXN<<2],Size[MAXN<<2];
	int CntId;
	void Init()
	{
		memset(Root,0,sizeof(Root));
		for(int i=1;i<=CntId;i++)
		{
			ls[i]=rs[i]=Size[i]=0;
		}
		CntId=0;
	}
	int Merge(int u1,int u2,int l,int r) 
	{
		if(u1&&u2)
		{
			if(u1==u2)
			return u1;
		}
		if(!u1||!u2)
		{
			return u1+u2;
		}
		if(l==r)
		{
			Size[u1]=max(Size[u1],Size[u2]);
			return u1;
		}
		int Mid=l+r>>1;
		ls[u1]=Merge(ls[u1],ls[u2],l,Mid);
		rs[u1]=Merge(rs[u1],rs[u2],Mid+1,r);
		Size[u1]=Size[ls[u1]]+Size[rs[u1]];
		return u1;
	}
	void Insert(int &u,int l,int r,int x)
	{
		if(u==0)
		{
			u=++CntId;
		}
		if(l==r)
		{
			Size[u]=1;
			return;
		}
		int Mid=l+r>>1;
		if(x<=Mid)
		Insert(ls[u],l,Mid,x);
		else
		Insert(rs[u],Mid+1,r,x);
		Size[u]=Size[ls[u]]+Size[rs[u]];
	}
	void Delete(int &u,int l,int r,int x)
	{
		if(u==0)
		{
			return;
		}
		if(l==r)
		{
			Size[u]=0;
			return;
		}
		int Mid=l+r>>1;
		if(x<=Mid)
		Delete(ls[u],l,Mid,x);
		else
		Delete(rs[u],Mid+1,r,x);
		Size[u]=Size[ls[u]]+Size[rs[u]];
	}
	int Query(int u,int l,int r,int x,int y)
	{
		if(!u)
		return 0;
		if(x<=l&&r<=y)
		{
			return Size[u];
		}
		int Mid=l+r>>1,Sum=0;
		if(x<=Mid&&y>=l)
		Sum+=Query(ls[u],l,Mid,x,y);
		if(x<=r&&y>=Mid+1)
		Sum+=Query(rs[u],Mid+1,r,x,y);
		return Sum;
	}
	bool Query(int u,int l,int r,int x)
	{
		if(u==0)
		{
			return false;
		}
		if(l==r)
		{
			if(Size[u])
			return true;
			else
			return false;
		}
		int Mid=l+r>>1;
		if(x<=Mid)
		return Query(ls[u],l,Mid,x);
		else
		return Query(rs[u],Mid+1,r,x);
	}
}trId[2],trLevel[2];//Id:0横1竖,Level[x]代表Color=x 
void Insert1(int x,int y,int z)//(x,y)这个空点插入编号为z的连通块 
{
	trId[0].Insert(trId[0].Root[z],1,ALL,GetId(0,x,y));
	trId[1].Insert(trId[1].Root[z],1,ALL,GetId(1,x,y));
}
void Insert2(int Nodeid,int z)//第Nodeid颗棋子插入编号为z的连通块 
{
	trLevel[Piece[Nodeid].Color].Insert(trLevel[0].Root[z],1,ALL,Piece[Nodeid].Level);
}

struct BingChaJi
{
	int father[MAXN],Min[MAXN],Max[MAXN];//father都要用到,Min和Max只在维护横竖线的时候用到
	void Init()
	{
		for(int i=1;i<=ALL;i++)
		{
			father[i]=i;
			Min[i]=1e9;Max[i]=0;
		}
	}
	int getfather(int x)
	{
		if(x!=father[x])
		father[x]=getfather(father[x]);
		return father[x];
	}
	void Merge(int x,int y)//x是father,y是son 
	{
		int fx=getfather(x),fy=getfather(y);
		if(fx==fy)
		return;
		father[fy]=fx;
	}
}Set[3];//Set[0]:连通块,Set[1]:横,Set[2]:竖 
void Init()
{
	memset(Edge,0,sizeof(Edge));
	memset(Ans,0,sizeof(Ans));
	memset(HaveNode,0,sizeof(HaveNode));
	trId[0].Init();
	trId[1].Init();
	trLevel[0].Init();
	trLevel[1].Init();
	Set[0].Init();
	Set[1].Init();
	Set[2].Init();
}
char opt[MAXN];
int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1};//对应Edge,上下左右
//方向op要比Edgeop大1,但相对位置不变 
//竖着的GetId要想好是0还是1,因为Edge永远都是0 
bool vis[MAXN];
int Mergedfs(int op,int x,int y,int fa)
{
	if(op==4)
	{
		vis[GetId(0,x,y)]=true;
		if(HaveNode[GetId(0,x,y+1)]==0&&Can(x,y+1)&&Edge[3][GetId(0,x,y)]==2)
		{
			Set[1].Merge(Set[1].getfather(GetId(0,x,fa)),Set[1].getfather(GetId(0,x,y+1)));
			return Mergedfs(op,x,y+1,fa);
		}
		else
		{
			return y;
		}
	}
	if(op==2)
	{
		vis[GetId(0,x,y)]=true;
		if(HaveNode[GetId(0,x+1,y)]==0&&Can(x+1,y)&&Edge[1][GetId(0,x,y)]==2)
		{
			Set[2].Merge(Set[2].getfather(GetId(0,fa,y)),Set[2].getfather(GetId(0,x+1,y)));
			return Mergedfs(op,x+1,y,fa);
		}
		else
		{
			return x;
		}
	}
}
//Id线段树是1-ALL,Level线段树是1-Q 
void dfs3(int x,int y,int fx,int fy)
{
	vis[GetId(0,x,y)]=true;
	trId[0].Insert(trId[0].Root[GetId(0,fx,fy)],1,ALL,GetId(0,x,y));
	trId[1].Insert(trId[1].Root[GetId(0,fx,fy)],1,ALL,GetId(1,x,y));//线段树只是记录竖编号,Root还是统一横编号! 
	for(int i=0;i<4;i++)
	{
		if(Edge[i][GetId(0,x,y)]==3&&vis[GetId(0,x+dx[i+1],y+dy[i+1])]==false)
		{
			if(HaveNode[GetId(0,x+dx[i+1],y+dy[i+1])]==0)
			{
				Set[0].Merge(Set[0].getfather(GetId(0,fx,fy)),Set[0].getfather(GetId(0,x+dx[i+1],y+dy[i+1])));
				dfs3(x+dx[i+1],y+dy[i+1],fx,fy);
			}
			else
			{
				int t=HaveNode[GetId(0,x+dx[i+1],y+dy[i+1])];
				trLevel[Piece[t].Color].Insert(trLevel[Piece[t].Color].Root[GetId(0,fx,fy)],1,Q,Piece[t].Level);
			}
		}
	}
}
void Read()
{
	scanf("%d%d%d",&N,&M,&Q);
	ALL=N*M; 
	Init();
	for(int i=1;i<=N;i++)
	{
		scanf("%s",&opt[1]);
		for(int j=1;j<M;j++)
		{
			//opt[j]表示(i,j)至(i,j+1)的边
			Edge[3][GetId(0,i,j)]=Edge[2][GetId(0,i,j+1)]=opt[j]-'0'; 
		}
	}
	for(int i=1;i<N;i++)
	{
		scanf("%s",&opt[1]);
		for(int j=1;j<=M;j++)
		{
			Edge[0][GetId(0,i+1,j)]=Edge[1][GetId(0,i,j)]=opt[j]-'0';
		}
	}
	for(int i=1;i<=Q;i++)
	{
		scanf("%d%d%d%d",&Piece[i].Color,&Piece[i].Level,&Piece[i].x,&Piece[i].y);
		Piece[i].Id=i;
		HaveNode[GetId(0,Piece[i].x,Piece[i].y)]=i;
	}
}
//除了trId[1]要用GetId(1)外其它都用GetId(0) 
int SolveEdge1(Node Now)
{
	int ans=0;
	int fx=Set[0].getfather(GetId(0,Now.x,Now.y));
	for(int i=0;i<4;i++)
	{
		if(Edge[i][GetId(0,Now.x,Now.y)]==1)
		{
			if(HaveNode[GetId(0,Now.x+dx[i+1],Now.y+dy[i+1])]==0)//旁边空格
			{
				if(trId[0].Query(trId[0].Root[fx],1,ALL,GetId(0,Now.x+dx[i+1],Now.y+dy[i+1]))==false)//空格,走3走不到 
				{
					ans++;
				}
			}
			else
			{
				if(CanEat(Now,Piece[HaveNode[GetId(0,Now.x+dx[i+1],Now.y+dy[i+1])]]))
				{
					int NowColor=Piece[HaveNode[GetId(0,Now.x+dx[i+1],Now.y+dy[i+1])]].Color,NowLevel=Piece[HaveNode[GetId(0,Now.x+dx[i+1],Now.y+dy[i+1])]].Level;
					if(trLevel[NowColor].Query(trLevel[NowColor].Root[fx],1,Q,NowLevel)==false)//吃子,走3吃不到 
					{
						ans++;
					}	
				}
			}
		}
	}
	return ans; 
}
vector<int>Eatid;
int SolveEdge2(Node Now)
{
	int ans=0;
	int l1,r1,l2,r2;//l1-r1横,l2-r2竖
	l1=Set[1].Min[Set[1].getfather(GetId(0,Now.x,Now.y))]; 
	r1=Set[1].Max[Set[1].getfather(GetId(0,Now.x,Now.y))]; 
	l2=Set[2].Min[Set[2].getfather(GetId(0,Now.x,Now.y))]; 
	r2=Set[2].Max[Set[2].getfather(GetId(0,Now.x,Now.y))]; 
	int fx=Set[0].getfather(GetId(0,Now.x,Now.y));
	//空格:能不能被3走到
	ans+=r1-l1+1-trId[0].Query(trId[0].Root[fx],1,ALL,GetId(0,Now.x,l1),GetId(0,Now.x,r1));//因为先Delete,所以trId包含了自身,而r1-l1+1刚好包含了自身,所以消掉了 
	ans+=r2-l2+1-trId[1].Query(trId[1].Root[fx],1,ALL,GetId(1,l2,Now.y),GetId(1,r2,Now.y));
	//吃子:最多4颗,能不能被3吃到 
	Eatid.clear();
	if(Edge[2][GetId(0,Now.x,l1)]==2)//能往左边走(不是边界,却不是l1,则说明是有棋子) 
	Eatid.push_back(GetId(0,Now.x,l1-1));
	if(Edge[3][GetId(0,Now.x,r1)]==2)
	Eatid.push_back(GetId(0,Now.x,r1+1));
	if(Edge[0][GetId(0,l2,Now.y)]==2)
	Eatid.push_back(GetId(0,l2-1,Now.y));
	if(Edge[1][GetId(0,r2,Now.y)]==2)
	Eatid.push_back(GetId(0,r2+1,Now.y));
	for(int i=0;i<Eatid.size();i++)
	{
		if(CanEat(Now,Piece[HaveNode[Eatid[i]]]))
		{
			int NowColor=Piece[HaveNode[Eatid[i]]].Color,NowLevel=Piece[HaveNode[Eatid[i]]].Level;
			if(trLevel[NowColor].Query(trLevel[NowColor].Root[fx],1,Q,NowLevel)==false)
			{
				ans++;
			}
		}
	}
	return ans;
}
int SolveEdge3(Node Now)
{
	int ans=0;
	int fx=Set[0].getfather(GetId(0,Now.x,Now.y));
	ans+=trId[0].Size[trId[0].Root[fx]]-trId[0].Query(trId[0].Root[fx],1,ALL,GetId(0,Now.x,Now.y));
	ans+=trLevel[Now.Color^1].Query(trLevel[Now.Color^1].Root[fx],1,Q,1,Now.Level-1);
	return ans;
}
vector<int>E2,E3;
void Del(Node Now)
{
	HaveNode[GetId(0,Now.x,Now.y)]=0;
	E3.clear();
	for(int i=0;i<4;i++)
	{
		if(Edge[i][GetId(0,Now.x,Now.y)]==3)
		{
			E3.push_back(GetId(0,Now.x+dx[i+1],Now.y+dy[i+1]));
		}
	}
	int ffx=Set[0].getfather(GetId(0,Now.x,Now.y));
	trId[0].Insert(trId[0].Root[ffx],1,ALL,GetId(0,Now.x,Now.y));
	trId[1].Insert(trId[1].Root[ffx],1,ALL,GetId(1,Now.x,Now.y));
	for(int i=0;i<E3.size();i++)
	{
		int fx=Set[0].getfather(GetId(0,Now.x,Now.y)),fy=Set[0].getfather(E3[i]);
		if(HaveNode[E3[i]])
		{
			int NowColor=Piece[HaveNode[E3[i]]].Color,NowLevel=Piece[HaveNode[E3[i]]].Level;
			trLevel[NowColor].Insert(trLevel[NowColor].Root[fx],1,Q,NowLevel);
			continue;
		}
		if(trId[0].Size[trId[0].Root[fy]])
		trId[0].Root[fx]=trId[0].Merge(trId[0].Root[fx],trId[0].Root[fy],1,ALL);
		if(trId[1].Size[trId[1].Root[fy]])
		trId[1].Root[fx]=trId[1].Merge(trId[1].Root[fx],trId[1].Root[fy],1,ALL);
		if(trLevel[0].Size[trLevel[0].Root[fy]])
		trLevel[0].Root[fx]=trLevel[0].Merge(trLevel[0].Root[fx],trLevel[0].Root[fy],1,Q);
		if(trLevel[1].Size[trLevel[1].Root[fy]])
		trLevel[1].Root[fx]=trLevel[1].Merge(trLevel[1].Root[fx],trLevel[1].Root[fy],1,Q);
		
		Set[0].Merge(fx,fy);
	}
	trLevel[Now.Color].Delete(trLevel[Now.Color].Root[ffx],1,Q,Now.Level);
		//从可以被吃的点变成可以被走过的空格 
		
	if(Edge[0][GetId(0,Now.x,Now.y)]==Edge[1][GetId(0,Now.x,Now.y)]&&Edge[0][GetId(0,Now.x,Now.y)]==2&&HaveNode[GetId(0,Now.x-1,Now.y)]==0&&HaveNode[GetId(0,Now.x+1,Now.y)]==0)
	{
		int fx=Set[2].getfather(GetId(0,Now.x-1,Now.y)),fy=Set[2].getfather(GetId(0,Now.x+1,Now.y));
		Set[2].Max[fx]=max(Set[2].Max[fx],Set[2].Max[fy]);//可以直接赋值,这样保险 
		Set[2].Min[fx]=min(Set[2].Min[fx],Set[2].Min[fy]);//肯定转移不了 
		Set[2].Merge(fx,fy);
		Set[2].Merge(Set[2].getfather(GetId(0,Now.x-1,Now.y)),Set[2].getfather(GetId(0,Now.x,Now.y)));
	}
	else
	{
		if(Edge[0][GetId(0,Now.x,Now.y)]==2&&HaveNode[GetId(0,Now.x-1,Now.y)]==0)
		{
			Set[2].Merge(Set[2].getfather(GetId(0,Now.x-1,Now.y)),Set[2].getfather(GetId(0,Now.x,Now.y)));
			Set[2].Max[Set[2].getfather(GetId(0,Now.x-1,Now.y))]=Now.x;
		}
		else
		{
			if(Edge[1][GetId(0,Now.x,Now.y)]==2&&HaveNode[GetId(0,Now.x+1,Now.y)]==0)
			{
				Set[2].Merge(Set[2].getfather(GetId(0,Now.x+1,Now.y)),Set[2].getfather(GetId(0,Now.x,Now.y)));
				Set[2].Min[Set[2].getfather(GetId(0,Now.x+1,Now.y))]=Now.x;
			}
			else
			{
				Set[2].Min[GetId(0,Now.x,Now.y)]=Set[2].Max[GetId(0,Now.x,Now.y)]=Now.x;
			}
		}
	}
	if(Edge[2][GetId(0,Now.x,Now.y)]==Edge[3][GetId(0,Now.x,Now.y)]&&Edge[2][GetId(0,Now.x,Now.y)]==2&&HaveNode[GetId(0,Now.x,Now.y-1)]==0&&HaveNode[GetId(0,Now.x,Now.y+1)]==0)
	{
		int fx=Set[1].getfather(GetId(0,Now.x,Now.y-1)),fy=Set[1].getfather(GetId(0,Now.x,Now.y+1));
		Set[1].Max[fx]=max(Set[1].Max[fx],Set[1].Max[fy]);
		Set[1].Min[fx]=min(Set[1].Min[fx],Set[1].Min[fy]);
		Set[1].Merge(fx,fy);
		Set[1].Merge(Set[1].getfather(GetId(0,Now.x,Now.y-1)),Set[1].getfather(GetId(0,Now.x,Now.y)));
	}
	else
	{
		if(Edge[2][GetId(0,Now.x,Now.y)]==2&&HaveNode[GetId(0,Now.x,Now.y-1)]==0)
		{
			Set[1].Merge(Set[1].getfather(GetId(0,Now.x,Now.y-1)),Set[1].getfather(GetId(0,Now.x,Now.y)));
			Set[1].Max[Set[1].getfather(GetId(0,Now.x,Now.y-1))]=Now.y;
		}
		else
		{
			if(Edge[3][GetId(0,Now.x,Now.y)]==2&&HaveNode[GetId(0,Now.x,Now.y+1)]==0)
			{
				Set[1].Merge(Set[1].getfather(GetId(0,Now.x,Now.y+1)),Set[1].getfather(GetId(0,Now.x,Now.y)));
				Set[1].Min[Set[1].getfather(GetId(0,Now.x,Now.y+1))]=Now.y;
			}
			else
			{
				Set[1].Min[GetId(0,Now.x,Now.y)]=Set[1].Max[GetId(0,Now.x,Now.y)]=Now.y;
			}
		}	
	}
}
void Solve()
{
	Read();
	LSHLevel();
	//预处理:
//2类边:
	for(int i=1;i<=ALL;i++)
	vis[i]=false;
	//横: 
	for(int i=1;i<=N;i++)
	{
		for(int j=1;j<=M;j++)
		{
			if(HaveNode[GetId(0,i,j)]==0&&vis[GetId(0,i,j)]==0)
			{
				Set[1].Min[GetId(0,i,j)]=j;
				Set[1].Max[GetId(0,i,j)]=Mergedfs(4,i,j,j);
			}
		}
	}
	//竖:
	for(int i=1;i<=ALL;i++)
	vis[i]=false;
	for(int i=1;i<=N;i++)
	{
		for(int j=1;j<=M;j++)
		{
			if(HaveNode[GetId(0,i,j)]==0&&vis[GetId(0,i,j)]==0)
			{
				Set[2].Min[GetId(0,i,j)]=i;
				Set[2].Max[GetId(0,i,j)]=Mergedfs(2,i,j,i);
			}
		}
	}
//3类边:
	for(int i=1;i<=ALL;i++)
	vis[i]=false;
	for(int i=1;i<=N;i++)
	{
		for(int j=1;j<=M;j++)
		{
			if(vis[GetId(0,i,j)]==0&&HaveNode[GetId(0,i,j)]==0)
			{
				dfs3(i,j,i,j);
			}
		}
	}
	//开始模拟 
	for(int i=Q;i>=1;i--)
	{
		Del(Piece[i]);
		Ans[i]=SolveEdge1(Piece[i])+
		SolveEdge2(Piece[i])+
		SolveEdge3(Piece[i]);
	}
	for(int i=1;i<=Q;i++)
	{
		printf("%d\n",Ans[i]);
	}
}

int main()
{
	InOut(1);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		Solve();
	}
}

草稿,而不是题解

chess:大体思路
棋盘行数:N
棋盘列数:M
边的类型:(0类)不能走,(1类)走一步,(2类)一直朝同方向走,(3类)随便走
棋子 颜色:0,1
棋子 个数:Q
棋子等级:[1,Q]
走子规则:只能走同一条边
吃子规则: 颜色不同,等级小于等于的棋子可以吃
求:放上一个棋子,能走到多少个位置

不吃子时:
倒着来看,先算当前棋子的答案:
1:3类边答案
2:去重
1类边:枚举这条边能到的4个点的id,线段树查一下有没有,没有则ans++
2类边:维护当前能到的最远点,左右横着编号,上下竖着编号(下一步就是边界或棋子,不包含下一步)
3类边:维护连通块,每次算完一个棋子都要合并-->如何合并?
去重:3类边开2个动态开点线段树,一个代表横着编号,一个代表竖着编号

  • 枚举周围1类边不在编号线段树里的点有多少个
  • 枚举2对方向,对于横着的方向,能走的有最左...左一格,右一格...最右,=r-l 然后在线段树横着那一棵查询[l,r]区间内的和,减去
  • 加上2线段树的根节点和
    注意两棵线段树均不包含当前讨论的点,题目要求不含当前点。

更新图的状态:
开 2 棵线段树,表示当前连通块能走到的白点的Level,黑点的Level(权值线段树)

把当前点周围能到达的不同的联通块对应的(4棵)线段树分别合并起来
并查集维护连通块编号。

2类边:并查集,维护最左最右、最上最下。删掉当前点后最多合并2次

综上:
4棵线段树:横着编号连通块,竖着编号连通块,连通块白点Level,连通块黑点Level(找的时候找严格小于当前讨论点的Level 的)
3个并查集:连通块,最左最右编号,最上最下编号

规定细节:最左最右最上最下编号不包含有棋子的点,即不能吃。吃棋子单独讨论,跟颜色线段树进行去重
编号线段树不包含可以吃的子
颜色线段树只能包含可以吃的子
删掉当前子之后合并出来的编号线段树要包含当前点,颜色线段树要去掉当前点

预处理:
把所有棋子放上去,预处理出:

找到极长横(竖)线,此段的father都是左边那个,然后Min都是左边,Max都是右边
合并的时候Min为左边(上面)的Min,Max为右边(下面)的Max
father[右(上)根]=左(下)根

找到每一个连通块,合并
对于每一个放棋子的点,看看周围的连通块,周围的连通块的线段树更新此点

posted @ 2022-08-11 20:10  0htoAi  阅读(48)  评论(2编辑  收藏  举报