2018 Multi-University Training Contest 4
Problem B. Harvest of Apples
题意:
给出n,m,求C(n,0)+C(n,1)+C(n,2)+……+C(n,m)的值。
分析;
首先定义F(n,m)=C(n,0)+C(n,1)+C(n,2)+……+C(n,m)。根据C(n,m)=C(n-1,m-1)+C(n-1,m),F(n,m)=C(n,0)+C(n-1,0)+C(n-1,1)+C(n-1,1)+C(n-1,2)+……+C(n-1,m-1)+C(n-1,m)=2*F(n-1,m)-C(n-1,m)。知道递推公式后,首先想到了矩阵快速幂,但是因为C(n-1,m)随着递推公式不停地变化,一直没有什么好办法。然后我们考虑其中某些项的计算结果是可以被其他项使用的,比如F(3,2)的结果就可以被F(5,2)使用,如果我们根据按照一定的规则排序询问后,让之前的计算结果可以被后面的询问使用,这样我们就可以大大地减少计算量。对于上述想法其实就是莫队算法的实现思想。
代码:
#include <map> #include <queue> #include <math.h> #include <string> #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; #define ll long long #define ull unsigned long long #define cls(x) memset(x,0,sizeof(x)) #define clslow(x) memset(x,-1,sizeof(x)) const int mod=1e9+7; const int maxn=1e5+100; int n,m,T,block_size; ll fac[maxn],inv[maxn],res[maxn]; struct Query { int n,m,id; bool operator < (const Query& rhs) const { if(n/block_size!=rhs.n/block_size){ return n/block_size<rhs.n/block_size; } return m<rhs.m; } }; Query q[maxn]; ll poww(ll a,ll k) { ll res=1; while(k) { if(k&1) res=res*a%mod; a=a*a%mod; k>>=1; } return res; } void init(int n) { fac[0]=fac[1]=1; for(int i=2;i<=n;i++) fac[i]=fac[i-1]*i%mod; //阶乘的逆元 inv[n]=poww(fac[n],mod-2); for(int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod; block_size=sqrt(1e5); } ll C(int n,int m) { return fac[n]*inv[m]%mod*inv[n-m]%mod; } void mul(ll& x,ll y) { x=x*y%mod; } void add(ll& x,ll y) { x=(x+y)%mod; } void sub(ll& x,ll y) { x=(x+mod-y)%mod; } void div(ll& x,int y) { x=(x*inv[y])%mod; } int main() { // freopen("in.txt","r",stdin); init(maxn-1); scanf("%d",&T); for(int i=1;i<=T;i++){ q[i].id=i; scanf("%d%d",&q[i].n,&q[i].m); } sort(q+1,q+T+1); ll ans=1; int L=0,R=0; for(int i=1;i<=T;i++){ //一定要先把n向上递推后,在计算m //先计算m,可能导致m的值大于当前的n值 while(R<q[i].n){ mul(ans,2); sub(ans,C(R,L)); R++; } while(R>q[i].n){ R--; add(ans,C(R,L)); div(ans,2); } while(L<q[i].m){ L++; add(ans,C(R,L)); } while(L>q[i].m){ sub(ans,C(R,L)); L--; } res[q[i].id]=ans; } for(int i=1;i<=T;i++){ printf("%lld ",res[i]); } return 0; }
Problem D. Nothing is Impossible
题意:
给出n个题目,m个学生,每个题目有1个正确选项,w个错误选项。现在学生可以从n个题目中任意挑选出S个题目,前提是m个学生中一定有一个人可以做对所有题目,问S的最大值是多少?
分析:
首先根据贪心策略,我们选题时题目的错误选项越少,肯定越好。然后确定一点,对于答错了题目的人,我们就不需要再管他们了,因为他们一定不可能全对。所以整体的思想就是先根据题目的错误选项个数从小到大排序,每次计算出一共有多少个学生答对这一题,然后再让这些答对的学生去答下一题,直到学生个数为0,中间答题的个数即是S的最大值。
代码:
#include <map> #include <queue> #include <math.h> #include <string> #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; #define ll long long #define ull unsigned long long #define cls(x) memset(x,0,sizeof(x)) #define clslow(x) memset(x,-1,sizeof(x)) const int maxn=1000; int n,m,T; struct Problem { int r,w; bool operator < (const Problem& rhs) const { return w<rhs.w; } }; Problem p[maxn]; int main() { // freopen("in.txt","r",stdin); scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d%d",&p[i].r,&p[i].w); } int cnt=0; sort(p+1,p+n+1); for(int i=1;i<=n;i++){ int all=p[i].r+p[i].w; //计算出这一题一共有多少学生答对 m=m/all+(m%all>p[i].w?m%all-p[i].w:0); if(m) cnt++; else break; } printf("%d ",cnt); } return 0; }