题解 F

传送门

考场上爆搜都写挂了……
这个题删点之前应该先缩下点,在同一个强连通分量里的点的顺序是不计入总方案数的

至于正解……
根据期望的线性性,每个点如果被删的话会产生1的贡献,所以删的总次数的期望就是每个点被删的概率之和
接下来就是统计每个点被删的概率
一个点被删的概率是它在它和所有能株连到它的点中首先被选的概率
所以答案是 \(\sum\limits_{i=1}^n\frac{1}{\text{能影响到点i的点数量}}\)

  • 根据根据期望的线性性把一个问题的答案拆分成这个问题中每个决策点的贡献×概率貌似是一类题……
    常见的套路是每次从剩下的点中选一个删除,每一轮每个点被删的概率可能受之前删点的方案影响
    一个常见的处理方法是考虑每个点产生贡献的概率与所有能在这个点之前被删除(并影响到这个点)的点被删除的概率的关系
    例题是这里这里
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1010
#define ll long long
//#define int long long

int n;
char s[N];
bool mp[N][N];
ll inv[3629000];
const ll mod=998244353;

// 因为没有考虑到有多个连通块时顺序会被考虑到这个爆搜中而爆炸了
namespace force{
	ll ans, cnt;
	int tim[N];
	void dfs2(int u, int bel) {
		tim[u]=bel;
		for (int v=1; v<=n; ++v) if (mp[u][v] && !tim[v]) dfs2(v, bel);
	}
	void dfs3(int u, int bel) {
		tim[u]=0;
		for (int v=1; v<=n; ++v) if (mp[u][v] && tim[v]==bel) dfs3(v, bel);
	}
	void dfs(int u) {
		int none=1;
		for (int i=1; i<=n; ++i) if (!tim[i]) {
			none=0;
			dfs2(i, i);
			dfs(u+1);
			dfs3(i, i);
		}
		if (none) ans+=u-1, ++cnt; //, cout<<u-1<<endl;
	}
	void solve() {
		inv[0]=inv[1]=1;
		for (int i=2; i<3629000; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		dfs(1);
		cout<<"cnt: "<<cnt<<endl;
		ans=ans*inv[cnt]%mod;
		printf("%lld\n", ans);
		exit(0);
	}
}

namespace task{
	ll inv[N], ans;
	int dfn[N], low[N], sta[N], bel[N], deg[N], siz[N], cnt[N], top, kind, tot;
	bool e[N][N], vis[N];
	bitset<N> s[N];
	queue<int> q;
	void tarjan(int u) {
		dfn[u]=low[u]=++tot;
		sta[++top]=u; vis[u]=1;
		for (int v=1; v<=n; ++v) if (mp[u][v]) {
			if (!dfn[v]) {
				tarjan(v);
				low[u]=min(low[u], low[v]);
			}
			else if (vis[v]) low[u]=min(low[u], dfn[v]);
		}
		if (dfn[u]==low[u]) {
			++kind;
			do {
				++siz[kind];
				s[kind][sta[top]]=1;
				bel[sta[top]]=kind;
				vis[sta[top--]]=0;
			} while (sta[top+1]!=u);
		}
	}
	void solve() {
		inv[0]=inv[1]=1;
		for (int i=2; i<=n; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		for (int i=1; i<=n; ++i) if (!dfn[i]) tarjan(i);
		for (int i=1; i<=n; ++i) {
			for (int j=1; j<=n; ++j) if (mp[i][j] && bel[i]!=bel[j] && !e[bel[i]][bel[j]]) {
				e[bel[i]][bel[j]]=1;
				++deg[bel[j]];
			}
		}
		for (int i=1; i<=kind; ++i) if (!deg[i]) q.push(i);
		// cout<<"bel: "; for (int i=1; i<=n; ++i) cout<<bel[i]<<' '; cout<<endl;
		while (q.size()) {
			int u=q.front(); q.pop();
			ans=(ans+siz[u]*inv[s[u].count()])%mod;
			// cout<<"u: "<<u<<' '<<s[u].count()<<endl;
			for (int v=1; v<=kind; ++v) if (e[u][v]) {
				// cout<<"v: "<<v<<' '<<deg[v]<<endl;
				s[v]|=s[u];
				if (--deg[v]==0) q.push(v);
			}
		}
		printf("%lld\n", ans);
		exit(0);
	}
}

signed main()
{
	freopen("f.in", "r", stdin);
	freopen("f.out", "w", stdout);

	scanf("%d", &n);
	for (int i=1; i<=n; ++i) {
		scanf("%s", s+1);
		for (int j=1; j<=n; ++j) mp[i][j]=(s[j]=='1');
	}
	// force::solve();
	task::solve();

	return 0;
}
posted @ 2021-10-18 09:09  Administrator-09  阅读(0)  评论(0编辑  收藏  举报