CF1368G Shifting Dominoes 题解

Statement

CF1368G Shifting Dominoes - 洛谷

Problem - 1368G - Codeforces

Solve

最开始看成 \(n,m\leq 2e5\) ,然后直接蒙蔽。。。

注意到骨牌的移动其实是很局限的,我们可以以此作为突破口

考虑一个空位经过一次移动会到哪里:

  1. 将图黑白染色后,黑格子只能通过骨牌移动到另外一个黑色格子,白色同理。
    也就是说,对于我们取下一个骨牌产生的两个空位,它们的移动是互不干扰的

  2. 这同时启发考虑空格的位置而不关心整个棋盘的具体状态

对于上图,我们考虑从最左侧向最右侧连边,也就是从这个格子向四周骨牌覆盖的另一个格子连边

那么,考虑新图有什么性质:

  1. 无环

可以画一画,对于任意的一种方案,我们首先可以转化为正方形的路径的形式(等价),例如,从这样

img

(图源:CF1368G Shifting Dominoes - 走看看 (zoukankan.com)

到这样:

即使红色路径可能不通,但是可以看到,中间被包围的区域始终是奇数

  1. 外向树森林

每个格子的入度为 \(0/1\)

\(----\)

这样,我们得到了这样一个新问题:

给定一个有向图森林,问从不同的树中选两个点的方案数

选两个点是因为刚刚讲的点 \(1\)

我们可以先 \(dfs\) 求出 \(dfn\) 序,那么,考虑对于每一张骨牌,对应到两颗子树 \(u,v\) ,抽掉这张骨牌的方案树就应该是 \(siz[u]*siz[v]\)

显然,方案数会有重叠,因为 \(u,v\) 分别是森林中某两颗树的一个子树,它们的树上父亲造成的贡献显然会重复

但是!\(1\)

那么,不妨把问题放到二维平面上,则每一对 \(u,v\) 就对应矩形 :

直接扫描线硬cao就可以了

Code

注意到 \(nm\leq 2e5\) ,所以用 \(vector\)

注意在这里 \(vector\) 需要 \(resize\)

注意 \(vector.resize(n)\) 之后 \(push\_back\) 会直接在第 \(n+1\) 的位置。

#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
const int N = 6e5+7;

struct Line{
	int x,y1,y2,fg;
	bool operator <(const Line&rhs)const{
		return x==rhs.x?(fg>rhs.fg):(x<rhs.x);
	}
}line[N];
struct Tree{
	int len,cnt;
}t[N<<3];
vector<int>Edge[N];
vector<pii>dom[N];
vector<char>mp[N];
int gp[N],siz[N],dfn[N];
int n,m,S,cnt,tim,tot;
bool vis[N];
char s[N];

int H(int i,int j){return (i-1)*m+j;}
void add(int u,int v){
	Edge[u].push_back(v),vis[v]=1;
	//printf("%d %d\n",u,v);
}
void dfs(int u){
	siz[u]=1,dfn[u]=++tim;
	for(auto v:Edge[u])
		dfs(v),siz[u]+=siz[v];
}
void pushup(int l,int r,int rt){
	if(t[rt].cnt>0)t[rt].len=r-l+1;
	else t[rt].len=t[ls].len+t[rs].len;
}
void build(int l,int r,int rt){
	t[rt].len=t[rt].cnt=0;
	if(l==r)return ;int mid=(l+r)>>1;
	build(l,mid,ls),build(mid+1,r,rs);
}
void change(int l,int r,int rt,int L,int R,int v){
	if(L>r||R<l)return; int mid=(l+r)>>1;
	if(L<=l&&r<=R)return t[rt].cnt+=v,pushup(l,r,rt),void();
	change(l,mid,ls,L,R,v),change(mid+1,r,rs,L,R,v);
	pushup(l,r,rt);
}

int main(){
	scanf("%d%d",&n,&m),S=n*m;
	for(int i=0;i<=n+1;++i)
		mp[i].resize(m+3);//
	for(int i=1;i<=n;++i){
		scanf("%s",s+1);
		for(int j=1;j<=m;++j)
			mp[i][j]=s[j];//
	} 
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j){
			if(mp[i][j+1]=='L'&&j+2<=m&&mp[i][j+2]=='R')add(H(i,j),H(i,j+2));
			if(mp[i][j-1]=='R'&&j-2>=1&&mp[i][j-2]=='L')add(H(i,j),H(i,j-2));
			if(mp[i+1][j]=='U'&&i+2<=n&&mp[i+2][j]=='D')add(H(i,j),H(i+2,j));
			if(mp[i-1][j]=='D'&&i-2>=1&&mp[i-2][j]=='U')add(H(i,j),H(i-2,j));
			if(!gp[H(i,j)]){
				gp[H(i,j)]=++cnt;
				if(mp[i][j]=='L')gp[H(i,j+1)]=cnt;
				if(mp[i][j]=='U')gp[H(i+1,j)]=cnt;
			}
			dom[gp[H(i,j)]].push_back(make_pair(i,j));
		}
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			if(!vis[H(i,j)])dfs(H(i,j));
	for(int i=1;i<=cnt;++i){
		int x1=dom[i][0].fi,y1=dom[i][0].se;
		int x2=dom[i][1].fi,y2=dom[i][1].se;
		int u=H(x1,y1),v=H(x2,y2);
		int l1=dfn[u],r1=dfn[u]+siz[u]-1;
		int l2=dfn[v],r2=dfn[v]+siz[v]-1;
		if((x1+y1)&1)swap(l1,l2),swap(r1,r2);
		line[i*2-1]={l1,l2,r2,1};//
		line[i*2]={r1+1,l2,r2,-1};
	}
	cnt<<=1;
	build(1,S,1);
	sort(line+1,line+cnt+1);
	long long ans=0;
	for(int i=1;i<=cnt;++i){
		ans+=(i>1&&line[i].x>=line[i-1].x)*(line[i].x-line[i-1].x)*t[1].len;
		change(1,S,1,line[i].y1,line[i].y2,line[i].fg);
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-08-25 14:43  _Famiglistimo  阅读(82)  评论(0编辑  收藏  举报