首页 > 其他分享 >CodeForces 1575F Finding Expected Value

CodeForces 1575F Finding Expected Value

时间:2024-08-15 23:27:11浏览次数:14  
标签:limits res ll CodeForces Expected maxn Finding sum mod

洛谷传送门

CF 传送门

考虑单个序列如何求答案。

考虑鞅与停时定理。定义一个局面的势能为 \(\sum\limits_{i = 0}^{K - 1} f(b_i)\),其中 \(f(x)\) 是一个关于 \(x\) 的函数,\(b_i\) 为 \(i\) 的出现次数。那么我们要构造 \(f(x)\),使得每次操作,局面势能期望减少 \(1\),那么期望步数就可以用初始局面的势能 \(\sum\limits_{i = 0}^{K - 1} f(b_i)\) 减去终止局面的势能 \(f(n)\) 计算出。

要求局面势能期望减少 \(1\),也就是:

\[\sum\limits_{i = 1}^K \frac{a_i}{n} \sum\limits_{j = 1}^K [i \ne j] \frac{1}{K} (f(a_i) - f(a_i - 1) + f(a_j) - f(a_j + 1)) = 1 \]

\[\sum\limits_{i = 1}^K \sum\limits_{j = 1}^K [i \ne j] a_i (f(a_i) - f(a_i - 1) + f(a_j) - f(a_j + 1)) = nK \]

\[\sum\limits_{i = 1}^K (K - 1) a_i (f(a_i) - f(a_i - 1)) + (n - a_i) (f(a_i) - f(a_i + 1)) = nK \]

不妨让对于每个 \(i\),求和号后面的式子都等于 \(n\)。也就是对于每个非负整数 \(x\),都满足:

\[(K - 1) x (f(x) - f(x - 1)) + (n - x) (f(x) - f(x + 1)) = n \]

移项得:

\[\frac{(K - 1) x (f(x) - f(x - 1)) + (n - x) f(x) - n}{n - x} = f(x + 1) \]

这里我们钦定 \(f(0) = 0\)。枚举 \(x\) 即可递推 \(f(x)\)。

再来考虑怎么计数。把 \(-1\) 替换成 \([0, K - 1]\) 的数后,有些数的出现次数会改变。不妨枚举一种原来出现次数为 \(j\) 的数替换完 \(-1\) 后出现次数变为 \(i\),设原来有 \(c_k\) 个出现次数为 \(k\) 的数,有 \(c\) 个 \(-1\),可得答案为:

\[\sum\limits_{i = 1}^n f(i) \sum\limits_{j = 0}^i c_j \binom{c}{i - j} (K - 1)^{c - (i - j)} \]

可以直接 MTT 做到 \(O(n \log n)\),但是注意到 \(c_j\) 有值的 \(j\) 的个数是 \(O(\sqrt n)\) 的,直接暴力枚举有值的位置计算就是 \(O(n \sqrt n)\)。

code
// Problem: F. Finding Expected Value
// Contest: Codeforces - COMPFEST 13 - Finals Online Mirror (Unrated, ICPC Rules, Teams Preferred)
// URL: https://codeforces.com/problemset/problem/1575/F
// Memory Limit: 512 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef __int128 lll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 600100;
const ll mod = 1000000007;

inline ll qpow(ll b, ll p) {
	ll res = 1;
	while (p) {
		if (p & 1) {
			res = res * b % mod;
		}
		b = b * b % mod;
		p >>= 1;
	}
	return res;
}

ll n, a[maxn], f[maxn], m, b[maxn], inv[maxn], fac[maxn], ifac[maxn], pw[maxn];

inline ll C(ll n, ll m) {
	if (n < m || n < 0 || m < 0) {
		return 0;
	} else {
		return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
	}
}

void solve() {
	scanf("%lld%lld", &n, &m);
	pw[0] = 1;
	for (int i = 1; i <= n; ++i) {
		pw[i] = pw[i - 1] * (m - 1) % mod;
	}
	inv[1] = 1;
	for (int i = 2; i <= n; ++i) {
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
	}
	fac[0] = ifac[0] = 1;
	for (int i = 1; i <= n; ++i) {
		fac[i] = fac[i - 1] * i % mod;
		ifac[i] = ifac[i - 1] * inv[i] % mod;
	}
	map<ll, ll> mp;
	ll cnt = 0;
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &a[i]);
		if (a[i] != -1) {
			++mp[a[i]];
		} else {
			++cnt;
		}
	}
	for (int i = 0; i < n; ++i) {
		f[i + 1] = ((m - 1) * i % mod * (f[i] - f[i - 1] + mod) % mod + (n - i) * f[i] - n + mod) % mod * inv[n - i] % mod;
	}
	b[0] = m - (ll)mp.size();
	for (pii p : mp) {
		++b[p.scd];
	}
	vector<int> vc;
	for (int j = 0; j <= n; ++j) {
		if (b[j]) {
			vc.pb(j);
		}
	}
	ll ans = 0;
	for (int i = 1; i <= n; ++i) {
		ll res = 0;
		for (int j : vc) {
			if (i - j > cnt) {
				continue;
			}
			if (i < j) {
				break;
			}
			res = (res + b[j] * C(cnt, i - j) % mod * pw[cnt - (i - j)]) % mod;
		}
		ans = (ans + res * f[i]) % mod;
	}
	ans = (ans * qpow(qpow(m, cnt), mod - 2) - f[n] + mod) % mod;
	printf("%lld\n", ans);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

标签:limits,res,ll,CodeForces,Expected,maxn,Finding,sum,mod
From: https://www.cnblogs.com/zltzlt-blog/p/18362050

相关文章

  • Codeforces 232 B Table 题解 [ 蓝 ] [ 分组背包 ] [ 组合数学 ] [ 循环节 ]
    Codeforces232BTable。蒟蒻模拟赛上场切的一道蓝,非常难以置信我竟然能做蓝题。这题的数据范围初看还是比较坑的,\(10^{18}\)的值域很容易让人往矩阵加速那方面想。实际上在列出转移方程式后,我们发现状态是二维的,无法使用矩阵加速(或者说这样做很麻烦)。思路首先观察到每个边长......
  • TypeError: add_code_sample_docstrings() got an unexpected keyword argument ‘tok
    可能是transformers的版本太高,可以考虑降版本。更推荐的解决方案:processor_class替换tokenizer_class注意:需要CTRLShiftF tokenizer_class,全部替换掉。参考链接:ALBEF(AlignbeforeFuse:VisionandLanguageRepresentationLearningwithMomentumDistillati)算法阅......
  • Codeforces Round 966 (Div. 3) VP
    A.PrimaryTask枚举所有情况即可voidsolve(){ strings; cin>>s; if(s.substr(0,2)!="10"||s.size()<3||s[2]=='0'||(s.size()<4&&s[2]<'2')){ cout<<"NO\n"; } else{......
  • [CodeForces] F. Color Rows and Columns
    ProblemLink Basedoninitialobservation,itseemsthatgreedilypickthesmallestrow/columnlengthworks.Butthelastexampletestcaseoutputs35whilegreedygives36.  Howyoushouldgofromthere:1.checkifyourgreedyimplementationisco......
  • Codeforces Round 966 (Div. 3)
    A-PrimaryTask给一个数\(x\),判断其是否形如\(\overline{ab}\)满足\(a=10,b\ge2\)且无前导零。模拟判断即可。code#include<bits/stdc++.h>usingnamespacestd;constintmaxn=3e5+3;intT;stringn;voidsolve(){ cin>>n; if((n=="1"||n=="10......
  • Codeforces Round894.D
    题目:D.IceCreamBalls题目链接:https://codeforces.com/contest/1862/problem/D思路:二分找到当所有冰淇淋球类型不同的情况下,假设记位k,满足k(k-1)/2<=n;此时不一定就等于k,所以考虑到有重复类型的冰淇淋球。当有两个重复类型的球时,可做不同类型的冰淇淋有k(k-1)/2+1,若有......
  • Codeforces Round 966 (Div. 3)
    Abstract第二次打CodeForce。A.PrimaryTaskIdea签到题。意思就是说给一个字符串,要你判断一下前两位是不是10,除去前两位后后面的部分是不是一个大于等于2的数(不能有前导零)。Code#include<bits/stdc++.h>usingnamespacestd;voidsolve(){stringtext;......
  • Codeforces Round 947 (Div. 1 + Div. 2)
    [传送门](Dashboard-CodeforcesRound947(Div.1+Div.2)-Codeforces)###A.枚举一个位置,把他前面和后面反转一下判断就行。###B.找到最小的数和最小的不是它的倍数的数当作$i$和$j$,判断合不合法即可。###C.不知道怎么就模出来了操作长度一定小于等于3,然后......
  • Codeforces Round 946 (Div. 3)
    ###G.一眼看上去很想个背包,然后发现好像不大能做。考虑反悔贪心。对于当前的$c_i$,如果到现在攒的钱足够买这个,那就直接买了它。如果钱不够,就从之前的买过的里面把一个最大的给退掉,然后买这个,优先队列维护即可。```c++#include<bits/stdc++.h>#defineintlonglongu......
  • Codeforces Round 964 (Div. 4)
    ###A.```c++#include<bits/stdc++.h>usingnamespacestd;constintN=1e6+7;voidsolve(){  intx;  cin>>x;  cout<<x/10+x%10<<endl;}intmain(){  intT;  cin>>T;  while(T--)solve();......