Tap

思路

  • 假设某种操作方式需要 x 步

  • 期望次数 = \(\sum \frac{1}{n}*\frac{1}{n-1}...*\frac{1}{n-x}*x\)

10 分

  • n \(\le\) 20 可以状压
  • 随便预处理一下 i 的水可以流到哪里, 状压一下, 叫做 val[i]
  • 第 i 位为 0 表示这个平台已经流上水了, 1 反之
  • 枚举可以从哪里转移到 dp[i]
    • 设 i 中有 cnt 个 0, 那么从每一个 0 那里转移过来的概率为 \(\frac{1}{cnt}\)
    • 可以转移到 i 的状态为 i | val[j] (i 的第 j 位为 0 )
    • 再对 dp[i] 加 1, 因为这一步要多打开一个水龙头

40 分

  • 根据期望的线性性, 可以把每一个平台分开考虑
  • 题意可以转化为枚举一个 n 的排列, 依次打开水龙头, 如果该平台在之前就被覆盖了, 那么跳过
  • \(ans = \sum 该平台被选择的概率\)
    • 被选择的概率就是 \(\frac{1}{能覆盖该平台的平台总个数(包括自己)}\)
    • 因为该平台能否被选择只和能覆盖他的平台数有关, 所以把他们拿出来单独考虑
      • 设一共有 cnt 个
      • 那么构成一个长度为 cnt 的序列, 该平台排第一个的概率就是 \(\frac{1}{cnt}\)

代码

10 分

#include <bits/stdc++.h> 
#define int long long
using namespace std;
const int MOD = 998244353;
const int N = 2e6 + 10;

int n;
int l[N], r[N], num[N];
int val[N], dp[N];
bool vis[N];
set <int> st[N];

int Q_pow(int a, int b){
	int ans = 1, p = a;
	while(b){
		if(b & 1){
			ans = (ans * p) % MOD;
		}
		b >>= 1;
		p = (p * p) % MOD;
	}
	return ans;
}

void Dfs(int x){
	vis[x] = 1;
	set <int> temp;
	for(auto i : st[x]){
		Dfs(i);
		for(auto j : st[i]){
			temp.insert(j);
		}
	}
	for(auto i : temp){
		st[x].insert(i);
	}
}

void Dp(){
	int up = (1 << n) - 1;
	for(int i = up - 1; i >= 0; i--){
		int cnt = 0;
		for(int j = 1; j <= n; j++){
			int ch = (i >> (j - 1)) & 1;
			cnt += (ch == 0);
		}
		for(int j = 1; j <= n; j++){
			int ch = (i >> (j - 1)) & 1;
			if(ch == 1){
				continue;
			}
			(dp[i] += dp[i | val[j]]) %= MOD;
		}
		(dp[i] *= Q_pow(cnt, MOD - 2)) %= MOD;
		(dp[i] += 1) %= MOD;
	}
	cout << dp[0] << "\n";
}

signed main(){
//	freopen("1.in", "r", stdin);
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> l[i] >> r[i];
		if(num[l[i]] != 0){
			st[i].insert(num[l[i]]);
		}
		if(num[r[i]] != 0){
			st[i].insert(num[r[i]]);
		}		
		for(int j = l[i]; j <= r[i]; j++){
			num[j] = i;
		}
	}
	for(int i = n; i >= 1; i--){
		if(!vis[i]){
			Dfs(i);
		}
		for(auto j : st[i]){
			val[i] |= 1 << (j - 1);
		}
		val[i] |= 1 << (i - 1);
	}
	Dp();
}

40 分

  • 这里使用 bitset 处理能覆盖 i 的平台数
    • bitset.set(pos, val) -> 把 pos 设为 0 / 1
#include <bits/stdc++.h> 
#define int long long
using namespace std;
const int MOD = 998244353;
const int N = 2e3 + 10;

int n;
int l, r, num[N], cnt[N];
bitset <N> st[N];

int Q_pow(int a, int b){
	int ans = 1, p = a;
	while(b){
		if(b & 1){
			ans = (ans * p) % MOD;
		}
		b >>= 1;
		p = (p * p) % MOD;
	}
	return ans;
}

signed main(){
//	freopen("1.in", "r", stdin);
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> l >> r;
		st[i].set(i, 1ll);
		st[i] |= st[num[l]];
		st[i] |= st[num[r]];
		for(int j = 1; j <= i; j++){
			cnt[j] += st[i][j];
		}
		for(int j = l; j <= r; j++){
			num[j] = i;
		}
	}
	
	int ans = 0;
	for(int i = 1; i <= n; i++){
        // cnt[i] 表示能覆盖 i 的平台数
		(ans += Q_pow(cnt[i], MOD - 2)) %= MOD;
	}
	cout << ans << "\n";
}

posted on 2024-04-03 23:08  Bubble_e  阅读(7)  评论(0)    收藏  举报