容斥的神
- [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≤yidpj∗(xi−xjxi−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+1CniCnjdpi,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+1Cnidpi,0+j=1∑n(−1)j+1Cnjdpj,0=2i=1∑n(−1)i+1Cni3i⋅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∑nj=1∑n(−1)i+j+1CniCnj⋅3⋅3(n−i)(n−j)=i=1∑nj=1∑n(−1)i(−1)j(−1)CniCnj3n2+13−in3−jn3ij=(−1)3n2+1i=1∑n(−1)iCni3−inj=1∑n(−1)jCnj3−jn3ij=(−1)3n2+1i=1∑n(−1)iCni3−inj=1∑nCnj(−1)j3(−n+i)j=(−1)3n2+1i=1∑n(−1)iCni3−inj=1∑nCnj(−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=0nCniaibn−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∑nCnj(−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)iCni3−inj=1∑nCnj(−3−n+i)j=(−1)3n2+1i=1∑n((−1)iCni3−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+1Cni3i3n(n−i)+(−1)3n2+1i=1∑n(−1)iCni3−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+1dpi−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+1dpi−1,k;dpi,j−1=k=1∑jdpi−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,l1l2结尾的方案加上l2l1,l2l1l2l_2l_1,l_2l_1l_2l2l1,l2l1l2结尾的.........
实现过程:将(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;
}