\(\mathbb{D}\rm escription\)

菲兹国为了庆祝每一年的史莱姆节,会准备一张大棋盘举行庆典,棋盘是一个 四连通 的连通格子图。不过史莱姆节使用的棋盘还满足一个特殊性质:对于 任意 两个格子,一定可以在不走出棋盘的条件下,先往一个方向走若干格,然后再往另一个方向走若干格而互相到达。棋盘不一定为矩形

一开始棋盘某些格子会被放上史莱姆。每只史莱姆初始体积为 \(1\),每秒钟史莱姆会独立且等概率地随机往四个方向中的一个方向移动一格。若有多只史莱姆在某一时刻到达了同一格子上,那么它们将合并成一个更大的史莱姆,其中合并后史莱姆的体积为所有被合并史莱姆的原始体积的和。

棋盘外的每个与边界相邻的格子都可能会有冒失的史莱姆不幸移入,所以每个这样的格子上都设置了一个餐位。如果史莱姆不幸落入了餐位所在的格子,那就难逃被抓住吃掉的命运咯~当所有史莱姆都移动出棋盘时,庆典就结束啦。

你的老婆糖糖幸运地参加了庆典,她最喜欢吃史莱姆了。可是糖糖笨笨的,不太会算数,你只好帮糖糖对于每个餐位,算出对应格子上能吃掉的史莱姆的总体积的期望(在取模 \(998244353\) 意义下)。由于输出可能会很多,你只需要告诉糖糖所有答案取模后再按位异或的结果。

棋盘大小至多为 \(40000\).

\(\mathbb{S}\rm olution\)

合并史莱姆的操作看上去就很不好搞,事实上,将史莱姆合并成一只史莱姆后它们向四个方向走的概率仍然是相同的,所以可以对每只史莱姆单独求解 —— 这就是一个经典的随机游走问题。

\(dp_{i,j}\) 为走到 \((i,j)\) 的期望次数,递推式就是(\(t_{i,j}\) 表示此格子初始是否存在史莱姆)

\[dp_{i,j}=\frac{1}{4}\cdot (dp_{i-1,j}+dp_{i+1,j}+dp_{i,j-1}+dp_{i,j+1})+t_{i,j} \]

\(t\) 趋近于无穷大时,期望次数是趋近于一个固定值的(虽然我不知道该怎么证明,好像要用到马氏链来着。

可以用高斯消元算出 \(\mathtt{dp}\) 值,那么对于一个餐位,它吃到史莱姆个数的期望就是 \(\frac{1}{4}\) 乘周围的 \(\mathtt{dp}\) 值。这看上去不太对,难道不会出现同一只史莱姆被计算多次的情况吗?事实上,由于 \(\mathtt{dp}\) 转移过程中并没有走到餐位,所以实际上 \(\frac{1}{4}\) 乘周围的 \(\mathtt{dp}\) 值就是某次走到 \((i,j)\) 的史莱姆再走到此餐位的答案。

对于 \(w\times h\) 的矩形的情况,可以考虑运用主元法:选取矩形较小的一维(第一行或第一列)为主元,这是为了保证主元个数 \(\le \sqrt n\),这里以第一列为例。我们要做的是将所有元用这 \(w\) 个主元表示 —— 事实上只需要用以 \((i,j)\) 为中心的方程表示元 \((i,j+1)\) 即可,这样我们还剩下最后一列的 \(w\) 个方程,将这些方程涉及的元用主元表示出来,就能解出主元。时间复杂度 \(\mathcal O(n\sqrt n)\).

不规则的棋盘我也不是太会,但还是大概讲一下。发现棋盘会存在一个十字架,大概长这样:

  xxx
xxxxxxxx
  xxxxx
  xx

总之就是一定 存在 一对最高点和最低点相对(在一条竖线上)、一对最左点和最右点相对,选取这样的两对点就形成了十字架。从十字架的中心四个方向向外拓展 \(\sqrt n\) 个点设为主元,其它的所有元都可以被这些主元表示,然后沿用上文的方法就可以解了?

$\mathbb{C}\rm ode $

# include <cstdio>
# include <cctype>
# define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while(!isdigit(s=getchar())) f|=(s=='-');
	for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
	return f? -x: x;
}
template <class T>
inline void write(T x) {
	static int writ[50], w_tp=0;
	if(x<0) putchar('-'), x=-x;
	do writ[++w_tp]=x-x/10*10, x/=10; while(x);
	while(putchar(writ[w_tp--]^48), w_tp);
}

# include <map>
# include <set>
# include <iostream>
using namespace std;
typedef long long ll;
typedef pair <int,int> par;

const int maxn = 205;
const int inf = 1000000;
const int mod = 998244353;
const int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};

inline int inv(int x,int y=mod-2) {
	int r=1;
	for(; y; y>>=1, x=1ll*x*x%mod)
		if(y&1) r=1ll*r*x%mod;
	return r;
} const int coe = inv(4);

int n; ll a[2005][2005];

void gauss(int n) {
	int j,tmp;
	for(int i=1;i<=n;++i) {
		for(j=i; !a[j][i] && j<=n; ++j);
		if(!a[j][i]) break; // IMPOSSIBLE
		if(i^j) swap(a[i],a[j]);
		int Inv = inv(a[i][i]);
		for(j=i+1;j<=n;++j) if(a[j][i]) {
			tmp = a[j][i]*Inv%mod;
			for(int k=i;k<=n+1;++k)
				a[j][k] = (a[j][k]-a[i][k]*tmp)%mod;
		}
	}
	for(int i=n;i;--i) {
		for(j=i+1;j<=n;++j)
			a[i][n+1] -= a[i][j]*a[j][j]%mod;
		a[i][i] = a[i][n+1]%mod*inv(a[i][i])%mod;
	}
}

namespace Subtask_1 {

par dots[2005];
set <par> st;
map <par,int> ID;

void work() {
	for(int i=1;i<=n;++i) {
		dots[i].first=read(9), dots[i].second=read(9);
		a[i][n+1]=-read(9), a[i][i]=-1;
		ID[dots[i]] = i;
	}
	for(int i=1;i<=n;++i) for(int j=0;j<4;++j) {
		int tx = dots[i].first+dir[j][0];
		int ty = dots[i].second+dir[j][1];
		if(ID.count(make_pair(tx,ty)))
			a[i][ID[make_pair(tx,ty)]] = coe;
		else st.insert(make_pair(tx,ty));
	}
	gauss(n); int id,all=0; ll ans;
	for(const auto& t:st) {
		ans = 0;
		for(int i=0;i<4;++i) {
			int tx = t.first+dir[i][0];
			int ty = t.second+dir[i][1];
			if(ID.count(make_pair(tx,ty)))
				id = ID[make_pair(tx,ty)],
				ans += a[id][id]*coe;
		}
		all ^= (ans%mod+mod)%mod;
	}
	print(all,'\n');
}

}

namespace Subtask_2 {

int all;
bool opt[40005];
ll cof[40005][205];
map <par,int> ID;

void transVal(ll* a,ll* b,int k) {
	for(int i=0;i<=all;++i) a[i] = (b[i]*k+a[i])%mod;
}

inline int getVal(int id) {
	int ret=cof[id][0];
	for(int i=1;i<=all;++i)
		ret = (cof[id][i]*a[i][i]+ret)%mod;
	return 1ll*ret*coe%mod;
}

void work() {
	int mn[2]={inf,inf}, mx[2]={-inf,-inf};
	for(int i=1;i<=n;++i) {
		int x=read(9), y=read(9);
		ID[make_pair(x,y)]=i, opt[i]=read(9);
		mn[0] = min(mn[0],x), mx[0] = max(mx[0],x);
		mn[1] = min(mn[1],y), mx[1] = max(mx[1],y);
	}
	bool D = false; int x,y,idx=1,id,nxt;
	if(mx[0]-mn[0]>mx[1]-mn[1]) D = true;
	all = mx[D]-mn[D]+1;
	for(int i=mn[D]; i<=mx[D]; ++i) {
		if(!D) x=i, y=1; else x=1, y=i;
		id = ID[make_pair(x,y)];
		cof[id][i-mn[D]+1] = 1;
	}
	for(int i=mn[D^1]; i<mx[D^1]; ++i) {
		for(int j=mn[D]; j<=mx[D]; ++j) {
			if(!D) x=j, y=i; else x=i, y=j;
			id = ID[make_pair(x,y)];
			if(!D) nxt = ID[make_pair(x,y+1)];
			else nxt = ID[make_pair(x+1,y)];
			cof[nxt][0] = opt[id]? -4: 0;
			transVal(cof[nxt],cof[id],4);
			for(int k=0;k<4;++k) {
				int tx = x+dir[k][0];
				int ty = y+dir[k][1];
				if((D && !(tx==x+1 && ty==y)) ||
			       (!D && !(tx==x && ty==y+1))) 
					transVal(cof[nxt],cof[ID[make_pair(tx,ty)]],-1);
			}
		}
	}
	for(int i=mn[D]; i<=mx[D]; ++i, ++idx) {
		if(!D) x=i, y=mx[1]; else x=mx[0], y=i;
		id = ID[make_pair(x,y)];
		transVal(a[idx],cof[id],-1);
		a[idx][all+1] = cof[id][0]-opt[id];
		for(int j=0;j<4;++j) {
			int tx = x+dir[j][0];
			int ty = y+dir[j][1];
			if(ty<=mx[1] && ty>=mn[1] && tx<=mx[0] && tx>=mn[0]) 
				nxt = ID[make_pair(tx,ty)],
				transVal(a[idx],cof[nxt],coe),
				a[idx][all+1] -= cof[nxt][0]*coe%mod;
		}
		a[idx][all+1] %= mod;
	}	
	gauss(--idx); int ans, candy=0;
	for(int i=mn[0]; i<=mx[0]; ++i)
		ans = getVal(ID[make_pair(i,mn[1])]),
		ans = (ans+mod)%mod, candy ^= ans,
		ans = getVal(ID[make_pair(i,mx[1])]),
		ans = (ans+mod)%mod, candy ^= ans;
	for(int i=mn[1]; i<=mx[1]; ++i)
		ans = getVal(ID[make_pair(mn[0],i)]),
		ans = (ans+mod)%mod, candy ^= ans,
		ans = getVal(ID[make_pair(mx[0],i)]),
		ans = (ans+mod)%mod, candy ^= ans;
	print(candy,'\n');
}

}

signed main() {
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	n=read(9);
	if(n^40000) Subtask_1::work();
	else Subtask_2::work();
	return 0;
}
posted on 2022-05-03 21:01  Oxide  阅读(126)  评论(0编辑  收藏  举报