8.8 容斥+组合数+期望

组合数

Count the Arrays

  1. 因为保证了\(m>n\)所以我们最终构造的数列中 应该从\(m\)个数里选出\(n-1\)个数 也就是\(C_m^{n-1}\) 因为我们选完\(n-1\)个数之后 最大值已经是确定的 所以不需要枚举最大值
  2. 最大值不能相邻(否则不满足单调递增和单调递减)剩余的\(n-2\)个数都可以作为重复的数字 那么方案个数为\((n-2)\)
  3. 钦定最大数字在中间 重复的数字在两侧 那么对于其他数字 每一个数字都可以归到左边或者右边 方案数为\(2^{n-3}\)(因为我们将左右确定好后 排序顺序也已经确定了)

所以最后答案就是\(C_m^{n-1}\times(n-2)\times 2^{n-3}\)

注意特判\(2\)的情况 小于\(3\)一定无解

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define eb emplace_back
#define int long long 
const int N = 2e5 + 5;
const int maxn = 2e5; 
const int mod = 998244353;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get()
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int ifac[N] , fac[N] , inv[N] , n , m;

void init()
{
	ifac[0] = ifac[1] = fac[0] = fac[1] = inv[0] = inv[1] = 1;
	for ( int i = 2 ; i <= maxn ; i ++ )
	{
		fac[i] = fac[i-1] * i % mod;
		inv[i] = ( ( mod - mod / i ) * inv[mod%i] + mod ) % mod;
		ifac[i] = ifac[i-1] * inv[i] % mod;
	}
}

inl int C ( int n , int m ) { return fac[n] * ifac[n-m] % mod * ifac[m] % mod; }

int ksm ( int base , int k )
{
	int res = 1;
	for ( ; k ; k >>= 1 , ( base *= base ) %= mod )
		if ( k & 1 ) ( res *= base ) %= mod;		
	return res;
}


signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	init();
	if ( n == 2 ) return cout << 0 << endl , 0;
	return cout << C ( m , n - 1 ) % mod * ksm ( 2 , n - 3 ) % mod * ( n - 2 ) % mod << endl , 0;
}

容斥

P2567 [SCOI2010] 幸运数字

可以先行预处理\([1,10^{10}]\)范围内的所有幸运数字

然而两个幸运号码对应的近似幸运号码可能有交集 考虑到这一点 就可以用容斥:

\(1\)个号码-选\(2\)个号码的\(lcm\)+选\(3\)个号码的\(lcm\)...

考虑用搜索解决问题 但直接做会\(T\) 考虑优化

  1. 首先将已经为倍数的幸运数字删除掉 防止重复统计
  2. 当前的\(lcm\)如果已经大于上界\(r\)就不再继续搜索
  3. 将留下来的幸运数字重新排序 使得\(lcm\)可以更快超过上界\(r\)

需要\(int128\)

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define eb emplace_back
#define lll long long 
#define int __int128
const int N = 2e5 + 5;
const int maxn = 2e5; 
const int mod = 998244353;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get()
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int l , r , vis[N] , ans;
vector<int> v , a;

int gcd ( int a , int b )
{
	if ( a % b == 0 ) return b;
	return gcd ( b , a % b );
}
int lcm ( int a , int b ) { return a * b / gcd ( a , b ); }

void init ( int x )
{
	if ( x > r ) return;
	v.eb(x) , init(x*10+8) , init(x*10+6);
}

void shai ()
{
	for ( int i = 0 ; i < v.size() ; i ++ )
	{
		if ( !vis[i] ) a.eb(v[i]);
		for ( int j = i + 1 ; j < v.size() ; j ++ )
			if ( v[j] % v[i] == 0 ) vis[j] = 1;
	}
}

inl int calc ( int ll , int rr , int val )
{
	rr /= val , ll = ll / val + ( ll % val != 0 );
	return rr - ll + 1;
}

void dfs ( int dep , int cnt , int val )
{
	if ( val > r ) return;
	if ( dep >= a.size() )
	{
		if ( cnt ) ans += calc ( l , r , val ) * ( ( cnt & 1 ) ? 1 : -1 );
		return;
	}
	dfs ( dep + 1 , cnt , val );
	int tmp = lcm ( val , a[dep] );
	if ( tmp <= r ) dfs ( dep + 1 , cnt + 1 , tmp );
}


signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	l = read() , r = read();
	init(6) , init(8) , shai();
	sort ( a.begin() , a.end() , greater<int>() );//从大到小枚举 更快超过上界r
	dfs ( 0 , 0 , 1 );
	cout << (lll)ans << endl;
	return 0;	
}
posted @ 2023-08-09 08:06  Echo_Long  阅读(11)  评论(0编辑  收藏  举报