文章目录
- Co-prime
- source
- solution
- code
- Character Encoding
- source
- solution
- code
- Tree and Constraints
- source
- solution
- code
- 「2017 山东一轮集训 Day7」逆序对
- source
- solution
- code
Co-prime
source
TTT组数据,给出𝐿,𝑅,𝑁𝐿, 𝑅, 𝑁L,R,N,求在区间[𝐿,𝑅][𝐿, 𝑅][L,R]中有多少个整数与𝑁𝑁N互质
solution
与SCOI2010幸运数字几乎一样的解决思路,甚至是弱化版
NNN最多含有111111个质因子
容斥原理
至少含000个NNN的质因子的个数−-−至少含111个NNN的质因子的个数+++至少含222个NNN的质因子的个数.........
完全可以dfs
搜索是哪些质因子,也就2112^{11}211
code
#include <cstdio>
#define int long long
int ans, m, l, r;
int prime[20];void divide( int n ) {for( int i = 2;i * i <= n;i ++ )if( n % i == 0 ) {prime[++ m] = i;while( n % i == 0 ) n /= i;}if( n > 1 ) prime[++ m] = n;
}int calc( int x ) {int L = l / x + ( l % x != 0 ), R = r / x;return R - L + 1;
}void dfs( int p, int cnt, int sum ) {if( p > m ) {if( cnt & 1 ) ans -= calc( sum );else ans += calc( sum );return;}dfs( p + 1, cnt, sum );dfs( p + 1, cnt + 1, sum * prime[p] );
}signed main() {int T, n;scanf( "%lld", &T );for( int Case = 1;Case <= T;Case ++ ) {scanf( "%lld %lld %lld", &l, &r, &n );ans = m = 0;divide( n );dfs( 1, 0, 1 );printf( "Case #%lld: %lld\n", Case, ans );}return 0;
}
Character Encoding
source
TTT组数据
有𝑚𝑚m个数,每个数的取值范围都是[0,𝑛−1][0, 𝑛−1][0,n−1]的整数。
求有多少种给这𝑚𝑚m个数赋值的方案,使得他们的和为𝑘𝑘k
方案数可能很大,请对998244353998244353998244353取模
solution
隔板法
把和为kkk,拆分成kkk个111的盒子,通过m−1m-1m−1块隔板分割成为mmm个数字
但是隔板法不能有000的情况
就强制每个数字自带111,也将取值范围+1+1+1变成[1,n][1,n][1,n],那么最后的和也对应变成k+mk+mk+m
根据隔板法可以得到在无限制的情况下方案数为(m+k−1m−1)\binom{m+k-1}{m-1}(m−1m+k−1)
容斥原理,强制iii个划分区域>n>n>n,隔板个数减少i∗ni*ni∗n
相当于已经有nnn个小球放到某个盒子内,然后把剩下小球放进mmm个盒子里面
code
#include <cstdio>
#define mod 998244353
#define int long long
#define maxn 200005
int T, n, m, k;
int fac[maxn], inv[maxn];int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans;
}void init( int N ) {fac[0] = inv[0] = 1;for( int i = 1;i <= N;i ++ )fac[i] = fac[i - 1] * i % mod;inv[N] = qkpow( fac[N], mod - 2 );for( int i = N - 1;i;i -- )inv[i] = inv[i + 1] * ( i + 1 ) % mod;
}int C( int n, int m ) {if( n < 0 || m < 0 || n < m ) return 0;else return fac[n] * inv[m] % mod * inv[n - m] % mod;
}signed main() {init( 2e5 );scanf( "%lld", &T );while( T -- ) {scanf( "%lld %lld %lld", &n, &m, &k );int ans = C( m + k - 1, m - 1 );for( int i = 1;i <= m;i ++ )if( i & 1 ) ans = ( ans - C( m, i ) * C( m + k - i * n - 1, m - 1 ) % mod ) % mod;else ans = ( ans + C( m, i ) * C( m + k - i * n - 1, m - 1 ) % mod ) % mod;printf( "%lld\n", ( ans + mod ) % mod );}return 0;
}
Tree and Constraints
source
给定一个含有𝑛𝑛n个节点的树,每一条边可以染成黑色或者白色
有𝑚𝑚m个限制条件,每一个限制条件包括两个节点(𝑢,𝑣)(𝑢, 𝑣)(u,v),限制为𝑢,𝑣𝑢, 𝑣u,v两个节点之间的路径中必须包含至少一个涂成黑色的边
问有多少种染色方法使得这mmm个条件同时满足
solution
容斥原理
有iii个限制满足不了,那么这iii个限制的路径的交都是白边,剩下的边无所谓
n≤50n\le 50n≤50,用long long
压二进制路径
求个路径的交
code
#include <cstdio>
#include <algorithm>
using namespace std;
#define int long long
#define maxn 55
vector < int > G[maxn];
int path[maxn], limit[maxn];void dfs( int u, int fa, int s ) {path[u] = s;for( auto v : G[u] ) {if( v == fa ) continue;else dfs( v, u, 1ll << v | s );}
}signed main() {int n, m;scanf( "%lld", &n );for( int i = 1, u, v;i < n;i ++ ) {scanf( "%lld %lld", &u, &v );G[u].push_back( v );G[v].push_back( u );}dfs( 1, 0, 0 );scanf( "%lld", &m );for( int i = 0, u, v;i < m;i ++ ) {scanf( "%lld %lld", &u, &v );limit[i] = path[u] ^ path[v];}int ans = 0;for( int i = 0;i < ( 1 << m );i ++ ) {int s = 0, cnt = 0;for( int j = 0;j < m;j ++ )if( 1 << j & i ) cnt ++, s |= limit[j];if( cnt & 1 ) ans -= 1ll << n - 1 - __builtin_popcountll( s );elseans += 1ll << n - 1 - __builtin_popcountll( s );}printf( "%lld\n", ans );return 0;
}
「2017 山东一轮集训 Day7」逆序对
source
给出𝑛,𝑘𝑛, 𝑘n,k,求有多少个𝑛𝑛n的全排列满足其逆序对数等于𝑘𝑘k
答案可能很大,请对998244353998244353998244353取模
solution
按从小到大的顺序加入的话,iii进去可能会造成xi∈[0,i−1]x_i\in[0,i-1]xi∈[0,i−1]的贡献
相当于求∑i=1nxi=k\sum_{i=1}^nx_i=k∑i=1nxi=k的多元方程组解个数
类似于第二题,只不过每个数的限制不太一样
容斥原理
无限制情况下(k+n−1n−1)\binom{k+n-1}{n-1}(n−1k+n−1)
钦定一些xi≥ix_i\ge ixi≥i
设限制的xi≥ix_i\ge ixi≥i的iii之和为sss
隔板法,方案数(k−s+n−1n−1)\binom{k-s+n-1}{n-1}(n−1k−s+n−1)
容斥系数,限制的个数(−1)m(-1)^m(−1)m
当s>ks>ks>k时对答案无贡献,只需要计算[1,n][1,n][1,n]中选若干个数,和为s,s∈[1,k]s,s\in[1,k]s,s∈[1,k]的个数
巧妙的构造方案:
-
每一次给一段数都+1+1+1,或者都+1+1+1后再放一个111,排除掉出现n+1n+1n+1的情况
这样的构造可以构造出numi∈[1,n]num_i\in[1,n]numi∈[1,n]的所有子集
设fi,j:f_{i,j}:fi,j: 选iii个数和为jjj的方案数
-
前iii个数全+1+1+1
+fi,j−i+f_{i,j-i}+fi,j−i
-
前i−1i-1i−1个数+1+1+1,新增一个111
+fi−1,j−i+f_{i-1,j-i}+fi−1,j−i
-
可能出现加111超过nnn的可能,强制前i−1i-1i−1个中有一个超成n+1n+1n+1
−fi−1,j−(n+1)-f_{i-1,j-(n+1)}−fi−1,j−(n+1)
code
#include <cmath>
#include <cstdio>
#define int long long
#define mod 1000000007
#define maxn 100005
int f[500][maxn];
int fac[maxn << 1], inv[maxn << 1];int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans;
}void init( int n ) {fac[0] = inv[0] = 1;for( int i = 1;i <= n;i ++ )fac[i] = fac[i - 1] * i % mod;inv[n] = qkpow( fac[n], mod - 2 );for( int i = n - 1;i;i -- )inv[i] = inv[i + 1] * ( i + 1 ) % mod;
}int C( int n, int m ) {if( n < 0 || m < 0 || n < m ) return 0;else return fac[n] * inv[m] % mod * inv[n - m] % mod;
}signed main() {int n, k;scanf( "%lld %lld", &n, &k );init( n + k );f[0][0] = 1;int lim = sqrt( k << 1 );for( int i = 1;i <= lim;i ++ )for( int j = i;j <= k;j ++ ) {f[i][j] = ( f[i][j - i] + f[i - 1][j - i] ) % mod;if( j <= n ) continue;else f[i][j] = ( f[i][j] - f[i - 1][j - n - 1] + mod ) % mod;}int ans = C( n + k - 1, n - 1 );for( int i = 1;i <= lim && i <= n;i ++ )for( int j = 1;j <= k;j ++ )if( i & 1 ) ans = ( ans - C( n + k - j - 1, n - 1 ) * f[i][j] % mod ) % mod;elseans = ( ans + C( n + k - j - 1, n - 1 ) * f[i][j] % mod ) % mod;printf( "%lld\n", ( ans + mod ) % mod );return 0;
}