容斥的神

  • [HAOI2008]硬币购物
    • problem
    • solution
    • code
  • CF559C Gerald and Giant Chess
    • problem
    • solution
    • code
  • [SCOI2010]幸运数字
    • problem
    • solution
    • code
  • CF997C Sky Full of Stars
    • problem
    • solution
    • code
  • 已经没有什么好害怕的了
    • problem
    • solution
    • code
  • [JLOI2015]骗我呢
    • problem
    • solution
    • code

容斥要么是dp预处理,要么是组合数推导(纯数学式子化简),要么是dfs搜索剪枝

[HAOI2008]硬币购物

problem

solution

先不管ddd的限制,预处理每一个sss的方案数

然后容斥−-−至少一个超出ddd限制+++至少两个超出ddd限制−-−至少三个超出ddd限制+++至少四个超出ddd限制

我们可以强制超出ddd限制,以超出d1d_1d1​为例

强制使用d1+1d_1+1d1​+1个c1c_1c1​,那么剩下s−(d1+1)c1s-(d_1+1)c_1s−(d1​+1)c1​​​的空间随便用即预处理的dps−(d1+1)c1dp_{s-(d_1+1)c_1}dps−(d1​+1)c1​​

code

#include <cstdio>
#define maxn 100005
#define int long long
int tot, c1, c2, c3, c4, d1, d2, d3, d4, s;
int dp[maxn];void init( int c ) {for( int i = c;i <= 1e5;i ++ )dp[i] += dp[i - c];
}signed main() {scanf( "%lld %lld %lld %lld %lld", &c1, &c2, &c3, &c4, &tot );dp[0] = 1;init( c1 ), init( c2 ), init( c3 ), init( c4 );while( tot -- ) {scanf( "%lld %lld %lld %lld %lld", &d1, &d2, &d3, &d4, &s );int ans = dp[s];if( c1 * ( d1 + 1 ) <= s ) ans -= dp[s - c1 * ( d1 + 1 )];if( c2 * ( d2 + 1 ) <= s ) ans -= dp[s - c2 * ( d2 + 1 )];if( c3 * ( d3 + 1 ) <= s ) ans -= dp[s - c3 * ( d3 + 1 )];if( c4 * ( d4 + 1 ) <= s ) ans -= dp[s - c4 * ( d4 + 1 )];if( c1 * ( d1 + 1 ) + c2 * ( d2 + 1 ) <= s ) ans += dp[s - c1 * ( d1 + 1 ) - c2 * ( d2 + 1 )];if( c1 * ( d1 + 1 ) + c3 * ( d3 + 1 ) <= s ) ans += dp[s - c1 * ( d1 + 1 ) - c3 * ( d3 + 1 )];if( c1 * ( d1 + 1 ) + c4 * ( d4 + 1 ) <= s ) ans += dp[s - c1 * ( d1 + 1 ) - c4 * ( d4 + 1 )];if( c2 * ( d2 + 1 ) + c3 * ( d3 + 1 ) <= s ) ans += dp[s - c2 * ( d2 + 1 ) - c3 * ( d3 + 1 )];if( c2 * ( d2 + 1 ) + c4 * ( d4 + 1 ) <= s ) ans += dp[s - c2 * ( d2 + 1 ) - c4 * ( d4 + 1 )];if( c3 * ( d3 + 1 ) + c4 * ( d4 + 1 ) <= s ) ans += dp[s - c3 * ( d3 + 1 ) - c4 * ( d4 + 1 )];if( c1 * ( d1 + 1 ) + c2 * ( d2 + 1 ) + c3 * ( d3 + 1 ) <= s ) ans -= dp[s - c1 * ( d1 + 1 ) - c2 * ( d2 + 1 ) - c3 * ( d3 + 1 )];if( c1 * ( d1 + 1 ) + c2 * ( d2 + 1 ) + c4 * ( d4 + 1 ) <= s ) ans -= dp[s - c1 * ( d1 + 1 ) - c2 * ( d2 + 1 ) - c4 * ( d4 + 1 )];if( c1 * ( d1 + 1 ) + c3 * ( d3 + 1 ) + c4 * ( d4 + 1 ) <= s ) ans -= dp[s - c1 * ( d1 + 1 ) - c3 * ( d3 + 1 ) - c4 * ( d4 + 1 )];if( c2 * ( d2 + 1 ) + c3 * ( d3 + 1 ) + c4 * ( d4 + 1 ) <= s ) ans -= dp[s - c2 * ( d2 + 1 ) - c3 * ( d3 + 1 ) - c4 * ( d4 + 1 )];if( c1 * ( d1 + 1 ) + c2 * ( d2 + 1 ) + c3 * ( d3 + 1 ) + c4 * ( d4 + 1 ) <= s ) ans += dp[s - c1 * ( d1 + 1 ) - c2 * ( d2 + 1 ) - c3 * ( d3 + 1 ) - c4 * ( d4 + 1 )];printf( "%lld\n", ans );}return 0;
}

CF559C Gerald and Giant Chess

problem

solution

到某个点(i,j)(i,j)(i,j)的方案数为(i+j−2i−1)\binom{i+j-2}{i-1}(i−1i+j−2​),将终点也看做一个黑格子

设dpidp_idpi​表示走到只经过第iii个黑格子的方案数,枚举可能经过的黑格子j(xj≤xi,yj≤yi)j(x_j\le x_i,y_j\le y_i)j(xj​≤xi​,yj​≤yi​)

容斥,dpi=dpi−∑j,xj≤xi,yj≤yidpj∗(xi−xj+yi−yjxi−xj)dp_{i}=dp_i-\sum_{j,x_j\le x_i,y_j\le y_i}dp_j*\binom{x_i-x_j+y_i-y_j}{x_i-x_j}dpi​=dpi​−∑j,xj​≤xi​,yj​≤yi​​dpj​∗(xi​−xj​xi​−xj​+yi​−yj​​)

code

#include <cstdio>
#include <algorithm>
using namespace std;
#define int long long
#define mod 1000000007
#define maxn 200000
struct node {int x, y;node(){}node( int X, int Y ) { x = X, y = Y; }
}pos[maxn];
int fac[maxn + 5], inv[maxn + 5], dp[maxn];
int h, w, n;bool cmp( node MS, node IS ) {return MS.x == IS.x ? MS.y < IS.y : MS.x < IS.x;
}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() {fac[0] = inv[0] = 1;for( int i = 1;i <= maxn;i ++ )fac[i] = fac[i - 1] * i % mod;inv[maxn] = qkpow( fac[maxn], mod - 2 );for( int i = maxn - 1;i;i -- )   inv[i] = inv[i + 1] * ( i + 1 ) % mod;
}int C( int n, int m ) {return fac[n] * inv[m] % mod * inv[n - m] % mod;
}signed main() {init();scanf( "%lld %lld %lld", &h, &w, &n );for( int i = 1;i <= n;i ++ )scanf( "%lld %lld", &pos[i].x, &pos[i].y );++ n; pos[n] = node( h, w );sort( pos + 1, pos + n + 1, cmp );for( int i = 1;i <= n;i ++ ) {int x = pos[i].x, y = pos[i].y;dp[i] = C( x + y - 2, x - 1 );for( int j = 1;j < i;j ++ )if( pos[j].x <= x && pos[j].y <= y )dp[i] = ( dp[i] - dp[j] * C( x - pos[j].x + y - pos[j].y, x - pos[j].x ) % mod + mod ) % mod;}printf( "%lld\n", dp[n] );return 0;
}

[SCOI2010]幸运数字

problem

solution

区间[l,r][l,r][l,r]中iii的倍数个数为⌊ri⌋−⌈li⌉+1\lfloor\frac{r}{i}\rfloor-\lceil\frac{l}{i}\rceil+1⌊ir​⌋−⌈il​⌉+1

容斥,至少选一个幸运数字−-−​至少选两个幸运数字+++​至少选三个幸运数字...∑i=1n(−1)i−1...\sum_{i=1}^n(-1)^{i-1}...∑i=1n​(−1)i−1​

如果直接暴搜1e101e101e10以内所有的幸运数字(204620462046个)的倍数是不可取的

需要搜索剪枝

  • 如果幸运数字iii是幸运数字jjj的倍数,那么iii就可以不用考虑了
  • 如果现在的幸运数字的lcmlcmlcm已经超过上限,也果断退出
  • 将幸运数字从大到小排序搜索,更容易爆出设定边界,减少前期不必要的分叉搜索

code

#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
#define maxn 3000
ll ans, L, R;
int tot, cnt;
ll id[maxn], num[maxn];
bool vis[maxn];void dfs1( int lim, int len, ll now, ll dig ) {if( len > lim ) {id[++ tot] = now;return;}dfs1( lim, len + 1, now + dig * 6, dig * 10 );dfs1( lim, len + 1, now + dig * 8, dig * 10 );
}void init() {for( int i = 1;i <= tot;i ++ ) {if( ! vis[i] ) num[++ cnt] = id[i];for( int j = i + 1;j <= tot;j ++ )if( id[j] % id[i] == 0 ) vis[j] = 1;}
} ll gcd( ll x, ll y ) {if( ! y ) return x;else return gcd( y, x % y );
}ll calc( ll x ) {ll l = L / x + ( L % x != 0 ), r = R / x;return r - l + 1;
}void dfs2( int x, int used, ll lcm ) {if( x > cnt ) {if( ! used ) return;ans += ( used & 1 ? 1 : -1 ) * calc( lcm );return;}dfs2( x + 1, used, lcm );ll t = lcm / gcd( lcm, num[x] );if( 1.0 * t * num[x] <= R )dfs2( x + 1, used + 1, t * num[x] );
}bool cmp( ll x, ll y ) { return x > y; }int main() {scanf( "%lld %lld", &L, &R );for( int i = 1;i <= 10;i ++ ) dfs1( i, 1, 0, 1 );init();sort( num + 1, num + cnt + 1, cmp );dfs2( 1, 0, 1 );printf( "%lld\n", ans );return 0;
}

CF997C Sky Full of Stars

problem

solution

容斥:∑i=0n∑j=0n[i+j>0](−1)i+j+1CniCnjdpi,j\sum_{i=0}^n\sum_{j=0}^n[i+j>0](-1)^{i+j+1}C_n^iC_n^jdp_{i,j}∑i=0n​∑j=0n​[i+j>0](−1)i+j+1Cni​Cnj​dpi,j​

dpi,j:idp_{i,j}:idpi,j​:i行jjj​列颜色相同的方案数

考虑对dpi,jdp_{i,j}dpi,j​进行求解,分类讨论

  • 只有行/列颜色相同,i=0/j=0i=0/j=0i=0/j=0(以行为例)

    有iii行颜色相同,每行颜色有333种选择,剩下的n∗(n−i)n*(n-i)n∗(n−i)格子是自由选择

    3i×3n∗(n−i)3^i\times 3^{n*(n-i)}3i×3n∗(n−i)

  • 有iii行jjj列颜色相同

    行与列有交,整个整体只有333种选择,剩下的(n−i)(n−j)(n-i)(n-j)(n−i)(n−j)格子是自由选择

    3×3(n−i)(n−j)3\times 3^{(n-i)(n-j)}3×3(n−i)(n−j)

dpi,j={3i×3n∗(n−i)j=03j×3n∗(n−j)i=03×3(n−i)(n−j)i≠0,j≠0dp_{i,j}= \begin{cases} 3^i\times 3^{n*(n-i)}&&j=0\\ 3^j\times 3^{n*(n-j)}&&i=0\\ 3\times 3^{(n-i)(n-j)}&&i≠0,j≠0 \end{cases} dpi,j​=⎩⎪⎨⎪⎧​3i×3n∗(n−i)3j×3n∗(n−j)3×3(n−i)(n−j)​​j=0i=0i​=0,j​=0​

i=0/j=0i=0/j=0i=0/j=0​的dpdpdp​​式子比较特殊,考虑单独计算
∑i=1n(−1)i+1Cnidpi,0+∑j=1n(−1)j+1Cnjdpj,0=2∑i=1n(−1)i+1Cni3i⋅3n(n−i)\sum_{i=1}^n(-1)^{i+1}C_n^idp_{i,0}+\sum_{j=1}^n(-1)^{j+1}C_n^jdp_{j,0}\\ =2\sum_{i=1}^n(-1)^{i+1}C_n^i3^i·3^{n(n-i)} i=1∑n​(−1)i+1Cni​dpi,0​+j=1∑n​(−1)j+1Cnj​dpj,0​=2i=1∑n​(−1)i+1Cni​3i⋅3n(n−i)
然后计算剩下的普通式子
∑i=1n∑j=1n(−1)i+j+1CniCnj⋅3⋅3(n−i)(n−j)=∑i=1n∑j=1n(−1)i(−1)j(−1)CniCnj3n2+13−in3−jn3ij=(−1)3n2+1∑i=1n(−1)iCni3−in∑j=1n(−1)jCnj3−jn3ij=(−1)3n2+1∑i=1n(−1)iCni3−in∑j=1nCnj(−1)j3(−n+i)j=(−1)3n2+1∑i=1n(−1)iCni3−in∑j=1nCnj(−3−n+i)j\sum_{i=1}^n\sum_{j=1}^n(-1)^{i+j+1}C_n^iC_n^j·3·3^{(n-i)(n-j)}\\ =\sum_{i=1}^n\sum_{j=1}^n(-1)^i(-1)^j(-1)C_n^iC_n^j3^{n^2+1}3^{-in}3^{-jn}3^{ij}\\ =(-1)3^{n^2+1}\sum_{i=1}^n(-1)^iC_n^i3^{-in}\sum_{j=1}^n(-1)^jC_n^j3^{-jn}3^{ij}\\ =(-1)3^{n^2+1}\sum_{i=1}^n(-1)^iC_n^i3^{-in}\sum_{j=1}^nC_n^j(-1)^j3^{{(-n+i)}^j}\\ =(-1)3^{n^2+1}\sum_{i=1}^n(-1)^iC_n^i3^{-in}\sum_{j=1}^nC_n^j(-3^{-n+i})^j i=1∑n​j=1∑n​(−1)i+j+1Cni​Cnj​⋅3⋅3(n−i)(n−j)=i=1∑n​j=1∑n​(−1)i(−1)j(−1)Cni​Cnj​3n2+13−in3−jn3ij=(−1)3n2+1i=1∑n​(−1)iCni​3−inj=1∑n​(−1)jCnj​3−jn3ij=(−1)3n2+1i=1∑n​(−1)iCni​3−inj=1∑n​Cnj​(−1)j3(−n+i)j=(−1)3n2+1i=1∑n​(−1)iCni​3−inj=1∑n​Cnj​(−3−n+i)j
有二项式定理:(a+b)n=∑i=0nCniaibn−i(a+b)^n=\sum_{i=0}^nC_n^ia^ib^{n-i}(a+b)n=∑i=0n​Cni​aibn−i

将a=1,b=−3−n+ia=1,b=-3^{-n+i}a=1,b=−3−n+i​​带入
(a+b)n=(1−3−n+i)n=∑j=0nCnj(−3−n+i)j1n−j(a+b)^n=\Big(1-3^{-n+i}\Big)^n=\sum_{j=0}^nC_n^j(-3^{-n+i})^j1^{n-j} (a+b)n=(1−3−n+i)n=j=0∑n​Cnj​(−3−n+i)j1n−j
二项式定理是从000的,所以要减去j=0j=0j=0的贡献
(−1)3n2+1∑i=1n(−1)iCni3−in∑j=1nCnj(−3−n+i)j=(−1)3n2+1∑i=1n((−1)iCni3−in((1−3−n+i)n−1))(-1)3^{n^2+1}\sum_{i=1}^n(-1)^iC_n^i3^{-in}\sum_{j=1}^nC_n^j(-3^{-n+i})^j\\ =(-1)3^{n^2+1}\sum_{i=1}^n\bigg((-1)^iC_{n}^i3^{-in}\Big((1-3^{-n+i})^n-1\Big)\bigg) (−1)3n2+1i=1∑n​(−1)iCni​3−inj=1∑n​Cnj​(−3−n+i)j=(−1)3n2+1i=1∑n​((−1)iCni​3−in((1−3−n+i)n−1))
两种情况综上,答案为
2∑i=1n(−1)i+1Cni3i3n(n−i)+(−1)3n2+1∑i=1n(−1)iCni3−in((1−3−n+i)n−1)2\sum_{i=1}^n(-1)^{i+1}C_n^i3^i3^{n(n-i)}+(-1)3^{n^2+1}\sum_{i=1}^n(-1)^iC_{n}^i3^{-in}\Big((1-3^{-n+i})^n-1\Big) 2i=1∑n​(−1)i+1Cni​3i3n(n−i)+(−1)3n2+1i=1∑n​(−1)iCni​3−in((1−3−n+i)n−1)

code

#include <cstdio>
#define int long long
#define mod 998244353
#define maxn 1000005
int n;
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() {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 ) {return fac[n] * inv[m] % mod * inv[n - m] % mod;
}signed main() {scanf( "%lld", &n );init();int ans = 0, ret = 0;for( int i = 1;i <= n;i ++ )if( i & 1 ) {ans = ( ans - C( n, i ) * qkpow( qkpow( 3, i * n ), mod - 2 ) % mod * ( qkpow( 1 - qkpow( qkpow( 3,  n - i ), mod - 2 ), n ) - 1 ) % mod ) % mod;ret = ( ret + C( n, i ) * qkpow( 3, n * ( n - i ) + i ) ) % mod;}else {ans = ( ans + C( n, i ) * qkpow( qkpow( 3, i * n ), mod - 2 ) % mod * ( qkpow( 1 - qkpow( qkpow( 3,  n - i ), mod - 2 ), n ) - 1 ) % mod ) % mod;ret = ( ret - C( n, i ) * qkpow( 3, n * ( n - i ) + i ) ) % mod;}ans = ans * -1 * qkpow( 3, n * n + 1 ) % mod;ret = ret * 2 % mod;printf( "%lld\n", ( ans + ret + mod + mod ) % mod );return 0;
}

已经没有什么好害怕的了

problem

solution

保证不会有重复的数字暗示了如果有k组糖果比药片能量大那么剩下的n-k组一定都是药片比糖果大

若恰好糖果比药片能量大的组数 比 药片比糖果能量大的组数多K组

反解出k=n+K2,kk=\frac{n+K}{2},kk=2n+K​,k​​表示糖果比药片能量大的组数

先将糖果和药片分别按能量大小排序,设fi,jf_{i,j}fi,j​​表示前iii​颗糖果中有jjj​组满足糖果能量大于药片

fi,j=fi−1,j+(cnti−(j−1))fi−1,j−1;;cnti:f_{i,j}=f_{i-1,j}+\Big(cnt_i-(j-1)\Big)f_{i-1,j-1};;cnt_i:fi,j​=fi−1,j​+(cnti​−(j−1))fi−1,j−1​;;cnti​:​ 药片能量小于糖果iii​​的药片个数

令gi=(n−i)!fn,ig_i=(n-i)!f_{n,i}gi​=(n−i)!fn,i​,表示把剩下的n−in-in−i个随意分配,得到糖果大于药片的组数至少为iii的方案数

果断标准广义容斥

  • fi:f_i:fi​: 至多选,gig_igi​恰好选

    fn=∑i=0n(ni)gi⇒gn=∑i=0n(−1)n−i(ni)fif_n=\sum_{i=0}^n\binom{n}{i}g_i\Rightarrow g_n=\sum_{i=0}^n(-1)^{n-i}\binom{n}{i}f_ifn​=∑i=0n​(in​)gi​⇒gn​=∑i=0n​(−1)n−i(in​)fi​​

  • fi:f_i:fi​: 至少选,gig_igi​恰好选

    fk=∑i=kn(ik)gi⇒gk=∑i=kn(−1)i−k(ik)fif_k=\sum_{i=k}^n\binom{i}{k}g_i\Rightarrow g_k=\sum_{i=k}^n(-1)^{i-k}\binom{i}{k}f_ifk​=∑i=kn​(ki​)gi​⇒gk​=∑i=kn​(−1)i−k(ki​)fi​​

一般fif_ifi​是非常好求的,反演求恰好的gig_igi​

ans=∑i=kn(−1)i−k(ik)gians=\sum_{i=k}^n(-1)^{i-k}\binom{i}{k}g_ians=∑i=kn​(−1)i−k(ki​)gi​

code

#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 2005
#define int long long
#define mod 1000000009
int n, k;
int candy[maxn], medicine[maxn], fac[maxn], inv[maxn];
int f[maxn][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() {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 ) {return fac[n] * inv[m] % mod * inv[n - m] % mod;
}signed main() {scanf( "%lld %lld", &n, &k );if( ( n + k ) & 1 ) return ! printf( "0\n" );k = ( n + k ) >> 1; init();for( int i = 1;i <= n;i ++ )scanf( "%lld", &candy[i] );for( int i = 1;i <= n;i ++ )scanf( "%lld", &medicine[i] );sort( candy + 1, candy + n + 1 );sort( medicine + 1, medicine + n + 1 );int cnt = 0;f[0][0] = 1; for( int i = 1;i <= n;i ++ ) {f[i][0] = 1;while( cnt < n && medicine[cnt + 1] < candy[i] ) cnt ++;for( int j = 1;j <= i;j ++ )f[i][j] = ( f[i - 1][j] + ( cnt - j + 1 ) * f[i - 1][j - 1] ) % mod;}int ans = 0;for( int i = k;i <= n;i ++ )if( ( i - k ) & 1 )ans = ( ans - C( i, k ) * fac[n - i] % mod * f[n][i] ) % mod;elseans = ( ans + C( i, k ) * fac[n - i] % mod * f[n][i] ) % mod;printf( "%lld\n", ( ans + mod ) % mod );return 0;
}

[JLOI2015]骗我呢

problem

solution

性质:每行必须严格递增,且每行mmm个 ,取值范围被限制于[0,m][0,m][0,m];所以一行有且只有一个比前面的大222​,剩下的一定都是比前面的大恰好111

设dpi,j:dp_{i,j}:dpi,j​: 第iii行没有出现的数为jjj的方案数

dpi,j=∑k=1j+1dpi−1,kdp_{i,j}=\sum_{k=1}^{j+1}dp_{i-1,k}dpi,j​=k=1∑j+1​dpi−1,k​

若第iii行没有出现jjj,那么(j,n](j,n](j,n]共n−jn-jn−j个数都是出现的,除去最大值放在最后一个,也至少需要n−j−1n-j-1n−j−1个数使得能够满足矩阵要求,所以上一行的上限就是j+1j+1j+1不出现,(j+1,n](j+1,n](j+1,n]共n−j−1n-j-1n−j−1个

dpi,j=∑k=1j+1dpi−1,k;dpi,j−1=∑k=1jdpi−1,kdp_{i,j}=\sum_{k=1}^{j+1}dp_{i-1,k};dp_{i,j-1}=\sum_{k=1}^jdp_{i-1,k}dpi,j​=k=1∑j+1​dpi−1,k​;dpi,j−1​=k=1∑j​dpi−1,k​

⇒dpi,j=dpi−1,j+1+dpi,j−1[j>0]\Rightarrow dp_{i,j}=dp_{i-1,j+1}+dp_{i,j-1}\qquad[j>0]⇒dpi,j​=dpi−1,j+1​+dpi,j−1​[j>0]

转化成二维图形上的关联↓

稍微变换一下(红线是形式上的辅助理解)

对称一下

这完全就是卡特兰数的几何意义解法,非常类似,我们套路的知道可以跟组合数扯上关系

从原点出发,不经过直线l1,l2l_1,l_2l1​,l2​,最后到达(n+m+1,n)(n+m+1,n)(n+m+1,n)的路径数

l1:y=x+1;l2:y=x−(m+2)l_1:y=x+1;l_2:y=x-(m+2)l1​:y=x+1;l2​:y=x−(m+2)

每一条路径都对应着从原点到翻折点的路径数

每条路径要么先碰l1l_1l1​要么先碰l2l_2l2​,所以用所有方案数减去先碰l1l_1l1​方案数减去先碰l2l_2l2​方案数

减去以l1l_1l1​开头的方案数,需要减去以l1,l1l2l_1,l_1l_2l1​,l1​l2​结尾的方案加上l2l1,l2l1l2l_2l_1,l_2l_1l_2l2​l1​,l2​l1​l2​结尾的.........

实现过程:将(x,y)(x,y)(x,y)沿直线l1l_1l1​​翻折,减去答案,将翻折点沿l2l_2l2​翻折,加上答案.........

l2l_2l2​开头的同理,(x,y)→(y−1,x+1)/(y+m+2,x−m−2)(x,y)\rightarrow(y-1,x+1)/(y+m+2,x-m-2)(x,y)→(y−1,x+1)/(y+m+2,x−m−2)

code

#include <cstdio>
#include <iostream>
using namespace std;
#define mod 1000000007
#define int long long
#define maxn 5000000
int fac[maxn + 5], inv[maxn + 5];
int n, m;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() {fac[0] = inv[0] = 1;for( int i = 1;i <= maxn;i ++ )fac[i] = fac[i - 1] * i % mod;inv[maxn] = qkpow( fac[maxn], mod - 2 );for( int i = maxn - 1;i;i -- )inv[i] = inv[i + 1] * ( i + 1 ) % mod;
}int C( int x, int y ) {if( x < 0 || y < 0 ) return 0;else return fac[x + y] * inv[x] % mod * inv[y] % mod;
}void work1( int &x, int &y ) {swap( x, y ), x --, y ++;
}void work2( int &x, int &y ) {swap( x, y ), x += ( m + 2 ), y -= ( m + 2 );
}signed main() {init();scanf( "%lld %lld", &n, &m );int x = n + m + 1, y = n, ans = C( x, y );while( x >= 0 && y >= 0 ) {work1( x, y );ans = ( ans - C( x, y ) ) % mod;work2( x, y );ans = ( ans + C( x, y ) ) % mod;}x = n + m + 1, y = n;while( x >= 0 && y >= 0 ) {work2( x, y );ans = ( ans - C( x, y ) ) % mod;work1( x, y );ans = ( ans + C( x, y ) ) % mod;}printf( "%lld\n", ( ans + mod ) % mod );return 0;
}

数论五之容斥——硬币购物,Gerald and Giant Chess,幸运数字,Sky Full of Stars,已经没有什么好害怕的了相关推荐

  1. Codeforces Round #313 (Div. 1) C. Gerald and Giant Chess DP

    C. Gerald and Giant Chess Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest ...

  2. [CF559C] Gerald and Giant Chess

    Gerald and Giant Chess 题意: 给你一个 h × w h\times w h×w的网格和 n n n个黑点,问你从 ( 1 , 1 ) (1, 1) (1,1)到 ( h , w ...

  3. cf559C. Gerald and Giant Chess(容斥原理)

    题意 $h \times w$的网格,有$n$个障碍点, 每次可以向右或向下移动 求从$(1, 1)$到$(h, w)$不经过障碍点的方案数 Sol 容斥原理 从$(1, 1)$到$(h, w)$不经 ...

  4. CodeForces - 560E Gerald and Giant Chess(组合数学+dp)

    题目链接:点击查看 题目大意:给出一个 n∗mn*mn∗m 的矩阵,其中有 kkk 个坏点,每次只能向右走或向下走,问从点 (1,1)(1,1)(1,1) 到点 (n,m)(n,m)(n,m) 共有多 ...

  5. bzoj 1042: [HAOI2008]硬币购物(dp+容斥)

    1042: [HAOI2008]硬币购物 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 2555  Solved: 1537 [Submit][St ...

  6. 洛谷P1450:硬币购物(背包、容斥)

    解析 呜呜呜不废啊 我只会跑n遍多重背包 感觉非常神仙的一道题 之所以只是蓝的可能是因为代码实现难度太低了吧 但感觉思想真的很难想到 也可能是我太菜了 容斥相关还是需要加强啊qwq 考虑如果没有硬币个 ...

  7. POJ 1150 The Last Non-zero Digit 数论+容斥

    POJ 1150 The Last Non-zero Digit 数论+容斥 题目地址:  POJ 1150 题意:  求排列P(n, m)后面第一个非0的数. 分析: 为了熟悉题目中的理论.我先做了 ...

  8. Educational Codeforces Round 37 G. List Of Integers (二分,容斥定律,数论)

    G. List Of Integers time limit per test 5 seconds memory limit per test 256 megabytes input standard ...

  9. 数学--数论--容斥定理完全解析(转)

    对容斥原理的描述 容斥原理是一种重要的组合数学方法,可以让你求解任意大小的集合,或者计算复合事件的概率. 描述 容斥原理可以描述如下: 要计算几个集合并集的大小,我们要先将所有单个集合的大小计算出来, ...

最新文章

  1. CCF CSP 201609-2 火车购票
  2. MySQL视图的创建、修改与删除
  3. 用 Jackson 来处理 JSON
  4. [LeetCode]Count of Range Sum
  5. HTML Viewer的定制
  6. C++ —— C++引用
  7. JavaScript-操作DOM对象-获得dom节点
  8. C#经典系列-键值对
  9. 【Linux】磁盘分区
  10. 转:使用脚本关闭订单头
  11. Linux Shell基础 Shell的输入重定向和输出重定向
  12. sqlserver的文件导入到mysql_SQLServer数据导入到MySQL方法介绍
  13. 学习java软件开发大概要多久?
  14. 【MATLAB】基本绘图函数(涵盖所有基本绘图指令)
  15. 人机交互-10-交互设计模型
  16. 计算机维修情况说明书,电脑坏了(电脑坏了情况说明)
  17. 程序员怎么做项目管理?
  18. 百度排名优化方案与想法
  19. linux c 开发数据,Linux c 开发
  20. 前端 | Chrome打不开HTTPS个人网站的解决办法

热门文章

  1. 多个php一个机器,如何利用docker在同一台机器上搭建多套php开发环境?
  2. python求导函数的值_python怎么实现函数求导
  3. c语言逆序数输三个数,C语言求助!一个三位数的逆序数,总是编不对
  4. python版本差异_python的版本的差别 2,3
  5. python读写文件实例_python读写文件的简单示例
  6. r.java没有生成_R.java 常见问题(R.java文件没有生成 )
  7. java main 声明_Java中main方面面试题
  8. php值比较大小,PHP_PHP浮点比较大小的方法,本文实例讲述了PHP浮点比较大 - phpStudy...
  9. android 开启一个定时线程_Android 定时任务刷新的多种实现方式
  10. git使用的基本流程_git命令的基本使用