暑假集训第二弹
A:不会做 // 留坑
———————————————————分————割————线———————————————————
B:CodeForces 577C Vasya and Petya's Game
传送门:http://acm.hust.edu.cn/vjudge/problem/241498/origin
题意:给一个数n 问你在[1,n]的区间内 有一个数m 你要猜哪几个数以后就肯定能得出答案。
你猜一个数x 会回答你 x是否能整除m
输入:一个n
输出:你要猜的那些数
思路:
从小往大猜,把倍数都标记一边,最后只被标记1次就是你需要猜的数
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int ans[1010],a[1010],tot=0; int main() { memset(a,0,sizeof a); int n; scanf("%d",&n); for(int i=2;i<=n;i++) if(!a[i]) for(int j=i;j<=n;j=j+i) a[j]++; for(int i=2;i<=n;i++) if(a[i]<2) ans[tot++]=i; printf("%d\n",tot); for(int i=0;i<tot;i++) printf("%d ",ans[i]); printf("\n"); }
———————————————————分————割————线———————————————————
C:POJ 2407 Relatives
传送门:http://poj.org/problem?id=2407
题意:给你一个n 问你有几个小于n的且与n互质的个数
输入:n
输出:个数
思路:欧拉函数
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; long long eular(long long n) { long long ans=n; for(int i=2;i*i<=n;i++) { if(n%i==0) { ans -= ans/i; while(n%i==0) n /= i; } } if(n>1) ans -= ans/n; return ans; } int main() { long long n; while(~scanf("%lld",&n) && n) printf("%lld\n",eular(n)); }
———————————————————分————割————线———————————————————
D:CodeForces 630B Moore's Law (水)
传送门:http://codeforces.com/problemset/problem/630/B
题意:给你2个数 a,b 问你 a*1.000000011^b
输入:两个数
输出:1个答案
思路:快速幂(然而直接pow也是能过的)
此处无代码
———————————————————分————割————线———————————————————
E:CodeForces 630F Selection of Personnel
传送门:http://codeforces.com/problemset/problem/630/F
题意:给你一个数n 要你求出C(5,n)C(6,n),C(7,n)
输入:一个数
输出:答案
思路:就是组合数 因为容易乘爆 所以要乘一个数除一个数
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int main() { int n; scanf("%d",&n); long long ans=0; for(int i=5;i<=7;i++) { long long now=1; for(int j=1;j<=i;j++) now=now*(n-j+1)/j; ans += now; } printf("%lld",ans); }
———————————————————分————割————线———————————————————
F:CodeForces 630G Challenge Pennants
传送门:http://codeforces.com/problemset/problem/630/G
题意:公司有5面A锦旗,3面B锦旗,先欲将这些锦旗全部发给n位员工,每位员工可以得到任意种类任意数量的锦旗,问有多少种发放方案
输入:员工数
输出:方案数
思路:
组合数问题 假设A锦旗放到1个人 2个人...5个人分别有 1 4 6 4 1的放法 然后乘上C(i,n)在求和
同理算出B锦旗的方法相乘
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int a[5]={1,4,6,4,1}; int b[3]={1,2,1}; long long C(long long n,long long m) { if(n>m-n) n=m-n; long long ans=1; for(int i=1;i<=n;i++) ans = ans*(m-i+1)/i; return ans; } int main() { int n; scanf("%d",&n); long long ans1=0,ans2=0; for(int i=0;i<5;i++) { if(i+1<=n) ans1 += C(i+1,n)*a[i]; else break; } for(int i=0;i<3;i++) { if(i+1<=n) ans2 += C(i+1,n)*b[i]; else break; } printf("%lld",ans1*ans2); }
———————————————————分————割————线———————————————————
G:CodeForces 630K Indivisibility
传送门:http://codeforces.com/problemset/problem/630/K
题意:给你一个数n,问你[1,n]中有多少个数不能被 2~10整除
输入:一个数
输出:答案
思路:
因为4 6 8 6 9 是2 3 的倍数 所以2~10相当于 2 3 5 7
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int main() { long long n,now=0; scanf("%lld",&n); now += n/2+n/3+n/5+n/7; now -= n/6+n/10+n/14+n/15+n/21+n/35; now += n/30+n/42+n/70+n/105; now -= n/210; printf("%lld",n-now); }
———————————————————分————割————线———————————————————
H题题目有错 跳过
———————————————————分————割————线———————————————————
I:POJ 2480 Longge's problem
传送门:http://poj.org/problem?id=2480
题意:给你1个数n 问你gcd(i, N) 1<=i <=N 的和是多少
思路:
这里是看了dalao们的题解才理解的// 需要注意的是sqrt枚举才行 i*i会T
dalao传送门:http://www.cnblogs.com/flipped/p/5690123.html
欧拉函数 φ(x)φ(x) :1到x-1有几个和x互质的数。
gcd(i,n)必定是n的一个约数。
若p是n的约数,那么gcd(i,n)==p的有φ(n/p)φ(n/p)个数,因为要使gcd(i,n)==p,i/p和n/p必须是互质的。
那么就是求i/p和n/p互质的i在[1,n]里有几个,就等价于 1/p,2/p,...,n/p 里面有几个和n/p互质,即φ(n/p)。
求和的话,约数为p的有φ(n/p),所以就是p*φ(n/p),同时把约数为n/p的加上去,i*i==n特判一下。
代码:
#include<cstdio> #include<cmath> #include<cstring> #include<iostream> using namespace std; typedef long long LL; LL phi(LL n) { LL res=n; for(int i=2;i<=sqrt(n);i++) { if(n%i==0) { res=res/i*(i-1); while(n%i==0) n/=i; } } if(n>1) res=res/n*(n-1); return res; } int main() { LL n; while(~scanf("%lld",&n)) { LL ans=0; for(int i=1;i<=sqrt(n);i++) { if(i*i==n) ans += i*phi(n/i); else if(n%i==0) ans += i*phi(n/i)+(n/i)*phi(i); } printf("%lld\n",ans); } return 0; }
———————————————————分————割————线———————————————————
J:HDU 5698 瞬间移动
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5698
题意:中文题
思路:
点从(1,1)移动到(m,n)最少要移动的步数为 min(m-1,n-1)
然后枚举几步走完全过程时横着的情况*竖着的情况
特例就是1步到位 答案为1 可以C(m-2,0)得到1
然后组合数求逆元就行了
ps:
网上有个证明把答案化为
C(m+n−4,n+2)=(n+m−4)!(n−2)!∗(m−2)!
代码;
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 100100 #define M 1000000007 typedef long long ll; ll A[N],B[N]; ll powinv(ll a, ll b) // 快速幂求乘法逆元 { ll ans = 1; a = a % M; while(b) { if(b & 1) ans = ans * a % M; a = a * a % M; b >>= 1; } return ans; } ll C(ll m,ll n) { return (A[m] * B[n]%M * B[m-n]%M) % M; } int main() { A[0]=1; for(int i=1;i<N;i++) A[i]=A[i - 1]*i % M; B[N-1] = powinv(A[N-1],M-2); for(int i=N-1;i>=1;i--) B[i-1]=B[i]*i % M; ll m,n; while(~scanf("%lld %lld",&m,&n)) { ll ans=0; for(int i=1;i<min(m,n);i++) ans += C(n-2,i-1)*C(m-2,i-1)%M; printf("%lld\n",ans%M); } }
———————————————————分————割————线———————————————————
K:HDU 3037 Saving Beans
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3037
题意:求在n棵树上摘不超过m颗豆子的方案,结果对p取模。
思路:求C(n+m,m)%p 数据很大用Lucas定理
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; ll f[100010]; void init(ll p) { f[0]=1; for(int i=1;i<=p;i++) f[i]=(f[i-1]*i)%p; } ll powinv(ll a, ll b, ll M) // 快速幂求乘法逆元 { ll ans = 1; a = a % M; while(b) { if(b & 1) ans = ans * a % M; a = a * a % M; b >>= 1; } return ans; } ll Lucas(ll n, ll m, ll p) { ll ans = 1; while (n && m) { ll nn = n%p, mm = m%p; if (nn < mm) return 0; ans = ans * f[nn] * powinv(f[mm]*f[nn-mm]%p, p-2, p)%p; n /= p; m /= p; } return ans; } int main() { int T; scanf("%d",&T); while(T--) { ll a,b,p; scanf("%lld %lld %lld",&a,&b,&p); init(p); printf("%lld\n",Lucas(a+b,b,p)); } }