Live2D

Solution -「CF 804F」Fake bullions

Description

  Link.

  给定 n 个点的竞赛图,第 i 个点代表了 si 个人,每个人(0-based)可能有真金条。此后在 t 时刻,对于图上任意边 u,v,若 u 中第 tmodsu 个人有金条(无论真假),且 v 中第 tmodsv 个人没有金条,那么后者获得一根假金条。

  足够长的时间后,所有人开始卖金条。真金条必定能出售,假金条可能能出售。把每个点按其代表的人卖出的金条总数从大到小排序,再随机从前 A 名中选 B 个点,求这 B 个点能构成的集合数量。对 109+7 取模。

  n5×103si2×106

Solution

  拼凑题嘛……和某道题一样过分 qwq。

  第一个问题:每个点最终拥有多少假金条。

  考虑一条边 u,v,显然若两人 x,y 在模 gcd(su,sv) 下同余,x 就能给 y 假金条。当然,金条的传递能够多次进行,所以可以缩点。对于第 i 个 SCC,计算出其中所有 sgcd,记为 di。那么 SCC 中某个点的第 x 个人有金条,当且仅当存在一个与 x 在模 di 意义下同余的 y 有金条。考虑两个 SCC 的连边 p,q,本质上是和点的连边一样的。这样就能计算出每个点的假金条数量了。


  接下来,考虑计算答案,每个点的售出金条对应了区间 [lu,ru]。钦定一个集合 B 在其中售出金条最小(多个最小则钦定编号最小)的点处被计数,枚举最小区间 [x,y],设有 p 个区间的 [l,r] 满足 l>y,它们是一定比当前区间大的;有 q 个区间 [l,r] 满足 lyr(注意上述钦定编号的情况,这里没有体现),再枚举在 q 个区间里选 i 个,则贡献为:

i(qi)(pBi1)

  统计答案就好 w。

Code

#pragma GCC optimize( 2 )

#include <cstdio>
#include <vector>

const int MAXN = 5e3, MAXL = 2e6, MOD = 1e9 + 7;
int n, s[MAXN + 5], A, B, mng[MAXN + 5], mxg[MAXN + 5];
int fac[MAXN + 5], ifac[MAXN + 5];
std::vector<int> adj[MAXN + 5];
std::vector<bool> gold[MAXN + 5];
int dfc, top, dfn[MAXN + 5], low[MAXN + 5], stk[MAXN + 5];
bool instk[MAXN + 5];
int scc, bel[MAXN + 5], siz[MAXN + 5], cnt[MAXN + 5];
std::vector<bool> apr[MAXN + 5];
int ans;

inline void chkmin ( int& a, const int b ) { if ( b < a ) a = b; }

inline int rint () {
	int x = 0; char s = getchar ();
	for ( ; s < '0' || '9' < s; s = getchar () );
	for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
	return x;
}

inline int gcd ( const int a, const int b ) { return b ? gcd ( b, a % b ) : a; }

inline int qkpow ( int a, int b, const int p = MOD ) {
	int ret = 1;
	for ( ; b; a = 1ll * a * a % p, b >>= 1 ) ret = 1ll * ret * ( b & 1 ? a : 1 ) % p;
	return ret;
}

inline void Tarjan ( const int u ) {
	dfn[u] = low[u] = ++ dfc, instk[stk[++ top] = u] = true;
	for ( int v: adj[u] ) {
		if ( ! dfn[v] ) {
			Tarjan ( v );
			chkmin ( low[u], low[v] );
		} else if ( instk[v] ) {
			chkmin ( low[u], dfn[v] );
		}
	}
	if ( dfn[u] == low[u] ) {
		int v; ++ scc;
		do {
			instk[v = stk[top --]] = false;
			bel[v] = scc;
		} while ( v ^ u );
	}
}

inline void input () {
	n = rint (), A = rint (), B = rint ();
	char tmp[MAXL + 5];
	for ( int i = 1; i <= n; ++ i ) {
		scanf ( "%s", tmp + 1 );
		for ( int j = 1; j <= n; ++ j ) {
			if ( tmp[j] ^ '0' ) {
				adj[i].push_back ( j );
			}
		}
	}
	for ( int i = 1; i <= n; ++ i ) {
		gold[i].resize ( s[i] = rint () );
		scanf ( "%s", tmp );
		for ( int j = 0; j < s[i]; ++ j ) {
			mng[i] += gold[i][j] = tmp[j] ^ '0';
		}
	}
}

inline void init () {
	fac[0] = 1;
	for ( int i = 1; i <= n; ++ i ) fac[i] = 1ll * i * fac[i - 1] % MOD;
	ifac[n] = qkpow ( fac[n], MOD - 2 );
	for ( int i = n - 1; ~ i; -- i ) ifac[i] = ( i + 1ll ) * ifac[i + 1] % MOD;
}

inline int comb ( const int n, const int m ) {
	return n < m ? 0 : 1ll * fac[n] * ifac[m] % MOD * ifac[n - m] % MOD;
}

inline void calcBound () {
	for ( int i = 1; i <= n; ++ i ) {
		if ( ! dfn[i] ) {
			Tarjan ( i );
		}
	}
	for ( int i = 1; i <= n; ++ i ) siz[bel[i]] = gcd ( s[i], siz[bel[i]] );
	for ( int i = 1; i <= scc; ++ i ) apr[i].resize ( siz[i] );
	for ( int i = 1; i <= n; ++ i ) {
		for ( int j = 0; j < s[i]; ++ j ) {
			if ( gold[i][j] ) {
				apr[bel[i]][j % siz[bel[i]]] = true;
			}
		}
	}
	std::vector<bool> tmp;
	for ( int i = scc; i > 1; -- i ) {
		int d = gcd ( siz[i], siz[i - 1] );
		tmp.clear (), tmp.resize ( d );
		for ( int j = 0; j < siz[i]; ++ j ) tmp[j % d] = tmp[j % d] | apr[i][j];
		for ( int j = 0; j < siz[i - 1]; ++ j ) apr[i - 1][j] = apr[i - 1][j] | tmp[j % d];
	}
	for ( int i = 1; i <= scc; ++ i ) for ( bool g: apr[i] ) cnt[i] += g;
	for ( int i = 1; i <= n; ++ i ) mxg[i] = s[i] / siz[bel[i]] * cnt[bel[i]];
}

inline void solve () {
	for ( int i = 1; i <= n; ++ i ) {
		int big = 0, crs = 0;
		for ( int j = 1; j <= n; ++ j ) {
			if ( i == j ) continue;
			if ( mxg[i] < mng[j] ) ++ big;
			else if ( mxg[i] <= mxg[j] - ( j < i ) ) ++ crs;
		}
		int l = std::max ( B - big - 1, 0 );
		int r = std::min ( crs, std::min ( B - 1, A - big - 1 ) );
		for ( int j = l; j <= r; ++ j ) {
			ans = ( ans + 1ll * comb ( crs, j ) * comb ( big, B - j - 1 ) ) % MOD;
		}
	}
}

int main () {
	input (), init ();
	calcBound ();
	solve ();
	printf ( "%d\n", ans );
	return 0;
}
posted @   Rainybunny  阅读(154)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示