8.8 容斥+组合数+期望
组合数
Count the Arrays
- 因为保证了\(m>n\)所以我们最终构造的数列中 应该从\(m\)个数里选出\(n-1\)个数 也就是\(C_m^{n-1}\) 因为我们选完\(n-1\)个数之后 最大值已经是确定的 所以不需要枚举最大值
- 最大值不能相邻(否则不满足单调递增和单调递减)剩余的\(n-2\)个数都可以作为重复的数字 那么方案个数为\((n-2)\)
- 钦定最大数字在中间 重复的数字在两侧 那么对于其他数字 每一个数字都可以归到左边或者右边 方案数为\(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\) 考虑优化
- 首先将已经为倍数的幸运数字删除掉 防止重复统计
- 当前的\(lcm\)如果已经大于上界\(r\)就不再继续搜索
- 将留下来的幸运数字重新排序 使得\(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;
}