Harvest of Apples (HDU多校第四场 B) (HDU 6333 ) 莫队 + 组合数 + 逆元
题意大致是有n个苹果,问你最多拿走m个苹果有多少种拿法。题目非常简单,就是求C(n,0)+...+C(n,m)的组合数的和,但是询问足足有1e5个,然后n,m都是1e5的范围,直接暴力的话肯定时间炸到奶奶都不认识了。当时想了好多好多,各种骚操作都想了一遍就是没想到居然是莫队....我用S(n,m)来记录C(n,0)+...+C(n,m)的和作为一个询问的答案
由组合数公式C(n,m) = C(n-1,m-1)+C(n-1,m)可以推的下面的式子
S(n,m) = S(n,m-1) + C(n,m);
S(n,m) = S(n,m+1) - C(n,m+1);
S(n,m) = 2*S(n-1)-C(n,m);
S(n,m) = ( S(n+1, m) + C(n,m) ) / 2;
这样发现居然是一个莫队的基本操作....然而只知道这几点也是不够的,因为组合数计算涉及到除法,所以得求逆元,还是阶乘的逆元,所以得先预处理出1~n的阶乘和它们的逆元,求逆元也是很耗时间的,但是我们可以通过递推一遍把阶乘的逆元求出来
设f(x)为x的逆元 那么 f(n!) = f( (n-1)!*n ) = f((n-1)!)*f(n); 将f(n)除过来 f(n!)*f(f(n)) = f((n-1)!) 因为x的逆元的逆元就是x本身, 因此得到公式f(n!)*n = f((n-1)!),这样我们只要求出最大的阶乘的逆元就可以从大到小推出所有逆元的值
就酱(顺便说一句不知道为啥hdu选c++会TLE或者WA,得用G++编译)
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <vector> 5 #include <queue> 6 #include <stack> 7 #include <math.h> 8 #include <string> 9 #include <algorithm> 10 #include <functional> 11 12 #define SIGMA_SIZE 26 13 #define lson rt<<1 14 #define rson rt<<1|1 15 #define lowbit(x) (x&-x) 16 #define foe(i, a, b) for(int i=a; i<=b; i++) 17 #define fo(i, a, b) for(int i=a; i<b; i++) 18 #define pii pair<int,int> 19 #pragma warning ( disable : 4996 ) 20 21 using namespace std; 22 typedef long long LL; 23 inline double dMax(double a, double b) { return a>b ? a : b; } 24 inline double dMin(double a, double b) { return a>b ? b : a; } 25 inline LL LMax(LL a, LL b) { return a>b ? a : b; } 26 inline LL LMin(LL a, LL b) { return a>b ? b : a; } 27 inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); } 28 inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; } //a*b = gcd*lcm 29 inline int Max(int a, int b) { return a>b ? a : b; } 30 inline int Min(int a, int b) { return a>b ? b : a; } 31 inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } 32 inline int lcm(int a, int b) { return a / gcd(a, b)*b; } //a*b = gcd*lcm 33 const LL INF = 0x3f3f3f3f3f3f3f3f; 34 const LL mod = 1e9+7; 35 const double eps = 1e-8; 36 const int inf = 0x3f3f3f3f; 37 const int maxk = 3e6 + 5; 38 const int maxn = 1e5+5; 39 40 int n, m, unit; 41 int belong[maxn]; 42 LL fac[maxn], inv[maxn],invof2; 43 LL ans[maxn]; 44 struct node { 45 int lhs, rhs, id; 46 }pp[maxn]; 47 48 bool cmp(const node& a, const node& b) 49 { 50 if (belong[a.lhs] == belong[b.lhs]) 51 return (belong[a.lhs]&1) ? a.rhs<b.rhs : a.rhs>b.rhs; 52 return a.lhs < b.lhs; 53 } 54 55 //设f(x)为x的逆元 56 //f(n!) = f( (n-1)!*n ) = f((n-1)!)*f(n); 57 //将f(n)除过来 f(n!)*f(f(n)) = f((n-1)!) 58 //因此可以从最大的n反向推回小的 59 LL getM( LL a, LL b, LL m) 60 { 61 LL ans = 1, base = a; 62 while (b) 63 { 64 if ( b & 1 ) 65 ans = (ans*base) % m; 66 base = (base*base) % m; 67 b >>= 1; 68 } 69 return ans; 70 } 71 72 void init() 73 { 74 fac[0] = fac[1] = 1; 75 inv[0] = inv[1] = 1; 76 foe(i, 2, maxn) 77 fac[i] = fac[i-1]*i%mod; 78 79 inv[maxn-1] = getM(fac[maxn-1], mod-2, mod); 80 invof2 = getM(2, mod-2, mod); 81 for ( int i = maxn-2; i >= 2; i-- ) 82 inv[i] = (inv[i+1]*(i+1))%mod; 83 } 84 85 LL C(int down, int up) 86 { 87 if ( up > down ) return 0; 88 return fac[down]*inv[up]%mod*inv[down-up]%mod; 89 } 90 91 int main() 92 { 93 init(); 94 int T; 95 cin >> T; 96 unit = sqrt(T); 97 98 foe(i, 1, T) 99 { 100 scanf("%d %d", &n, &m); 101 pp[i].lhs = n; pp[i].rhs = m; 102 pp[i].id = i; 103 belong[i] = i/unit+1; 104 } 105 sort(pp+1, pp+1+T, cmp); 106 107 //(l代表n, r代表m) 108 int l = 1, r = 0; 109 LL sum = 1; 110 111 for ( int i = 1; i <= T; i++ ) 112 { 113 while (r < pp[i].rhs) { 114 r++; 115 sum = (sum + C(l, r))%mod; 116 } 117 118 while (r > pp[i].rhs) { 119 sum = (sum - C(l, r)+mod)%mod; 120 r--; 121 } 122 123 while (l < pp[i].lhs) { 124 sum = ((LL)2*sum - C(l, r)+mod)%mod; 125 l++; 126 } 127 128 while (l > pp[i].lhs) { 129 l--; 130 sum = (sum+C(l,r))%mod*invof2%mod; 131 } 132 133 ans[pp[i].id] = sum; 134 } 135 136 foe(i, 1, T) 137 printf("%lld\n", ans[i]); 138 return 0; 139 }
什么时候能够不再这么懒惰