洛谷U425177 TopCoder12330 CoinsGame 题解

洛谷U425177 TopCoder12330 CoinsGame 题解


知识点

使用并查集与 BFS,并需要一点容斥的思想。


分析

通过题意,我们可以知道他是要我们求使一些硬币(至少一个)掉出字符数组外,同时使另一些硬币(至少一个)仍然留在字符数组上的摆放方案数。

我们注意到,题目写到“存在一系列操作序列”,因此我们可知只要把不合法的方案数从总方案数中减去即可。

那不合法的方案怎么求呢?

从不合法的定义出发:“某一种初始放置方案使得存在一系列操作序列可以从该放置方案中获胜”,那怎么才会不存在呢,其实就是说:一组硬币,如果掉出的话,就全部掉出;否则,就一个都不掉出。

我们可以再反过来看:把有可能错开掉出时间的点连一条边,然后 BFS 下去,就可以把这种情况排除,剩下没连边的都是一定会在一起掉出的点,我们用并查集把他们缩成一个集合。那么对于这个集合,设其大小为 \(siz\),不合法情况即为 \(2^{siz}-1\)(减 \(1\) 是由于不能为空),同理,总方案数即为 \(2^{cnt}-1\),其中 \(cnt\) 是空格总数。


CODE

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000009
#define F first
#define S second
#define Pii pair<int,int>
#define add(a,b) ((a)+(b)>=(MOD)?((a)+(b)-(MOD)):((a)+(b)<0?((a)+(b)+(MOD)):((a)+(b))))
#define mul(a,b) ((long long)(a)*(b)%(MOD))
#define toadd(a,b) ((a)=add((a),(b)))
#define tomul(a,b) ((a)=mul((a),(b)))
#define RCL(a,b,c,d) memset((a),(b),sizeof(c)*(d))
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(register int i=(a);i>=(b);--i)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
const int N=50+10,M=N*N;
int n,m,cnt;
bool a[M];
bool c[N][N];
int id[N][N];
void Scan() {
	cin>>n>>m;
	cnt=0;
	FOR(i,1,n)FOR(j,1,m) {
		char C;
		cin>>C;
		c[i][j]=(C=='.'),a[id[i][j]=(i-1)*m+(j-1)+1]=c[i][j],cnt+=c[i][j];
	}
}

int s;
int dx[4]= {1,-1,0,0},dy[4]= {0,0,1,-1},pw[M];
int nxt[M][4];
struct DSU {
	int fa[M],siz[M];
	int & operator [](int i) {
		return siz[i];
	}
	void init(int n) {
		FOR(i,1,n)fa[i]=i,siz[i]=1;
	}
	int get(int x) {
		return fa[x]^x?fa[x]=get(fa[x]):x;
	}
	void merge(int u,int v) {
		int x=get(u),y=get(v);
		if(x==y)return;
		fa[y]=x,siz[x]+=siz[y];
	}
} D;
void Init() {
	D.init(s=n*m);
	FOR(i,1,n)FOR(j,1,m)FOR(k,0,3) {
		int x=i+dx[k],y=j+dy[k];
		if(x<1||y<1||x>n||y>m)nxt[id[i][j]][k]=-1;
		else if(c[x][y])nxt[id[i][j]][k]=id[x][y];
		else nxt[id[i][j]][k]=id[i][j];
	}
}

queue< Pii > q;
bool vis[M][M],edge[M][M];
void push(int u,int v) {
	if(!vis[u][v])q.push({u,v}),vis[u][v]=1;
}
bool check(int u,int d) {
	return (~nxt[u][d])&&(nxt[u][d]!=u);
}
void Bfs() {
	RCL(vis,0,vis,1),RCL(edge,0,edge,1);
	FOR(i,1,s)if(a[i])FOR(j,1,s)if(a[j])FOR(k,0,3)if((nxt[i][k]==-1)^(nxt[j][k]==-1))push(i,j);
	while(!q.empty()) {
		Pii u=q.front();
		q.pop();
		edge[u.F][u.S]=1;
		FOR(i,0,3) {
			if(!check(u.F,i)&&check(u.S,i^2))push(u.F,nxt[u.S][i^2]);
			if(check(u.F,i^2)&&!check(u.S,i))push(nxt[u.F][i^2],u.S);
			if(check(u.F,i^2)&&check(u.S,i^2))push(nxt[u.F][i^2],nxt[u.S][i^2]);
		}
	}
}

void Build() {
	FOR(i,1,s)if(a[i])FOR(j,1,s)if(a[j]&&!edge[i][j])D.merge(i,j);
}

int ans;
void Print() {
	ans=pw[cnt]-1;
	FOR(i,1,s)if(a[i]&&D.get(i)==i)toadd(ans,MOD+1-pw[D[i]]);
	cout<<ans<<endl;
}

signed Cmain() {
	Scan();
	Init();
	Bfs();
	Build();
	Print();
	return 0;
}

int T;
void CInit() {
	pw[0]=1;
	FOR(i,1,M-5)pw[i]=mul(pw[i-1],2);
}
signed main() {
	CInit();
	for(cin>>T; T; --T)Cmain();
	return 0;
}

最劣时间复杂度 \(O(Tn^2m^2)\)


提示

注意 细节 哦(坏笑)。

posted @ 2024-04-22 19:06  Add_Catalyst  阅读(33)  评论(0)    收藏  举报