比完赛就没做题了,现在补上
Hdu 5407 CRB and Candies
链接
CRB and Candies
题意
给定一个数N,求LCM(C(N,0),C(N,1),…,C(N,N))
分析
令g(N) = LCM(C(N,0),C(N,1),…,C(N,N)),f(n) = LCM(1,2,…,n)
则g(n) = f(n+1)/(n+1) 为所求答案 (虽然不知道怎么证明…)
求最小公倍数可以用分解素因子思想,最小公倍数等于它们所有的质因数的乘积(如果有几个质因数相同,则比较两数中哪个数有该质因数的个数较多,乘较多的次数)
也可以用结论:$f(1)=1,若n=p^k,f(n)=f(n-1)*p,否则f(n)=f(n-1)$
这个结论也是用的分解质因子的思想,设num[i]为数1到n素因子i的最大个数,
若$n=p^k$,则num[i]=k,而num[n-1]=k-1,所以只需在f(n-1)基础上乘一个p即可,
否则$n=p_1^{k_1}*p_2^{k_2}* \cdots *p_n^{k_n},则num[ p_i ] >= k_i$,即不需要乘上额外的素因子
求完LCM还需要除以(n+1),最终答案还要取余,本题数据量较大,还涉及到除法和取余,可以用费马小定理,由定理得:a^(p-1)≡1%p —> a^(p-2)≡(1/a)%p,因为p很大,需要用快速幂取余来做
利用分解素因子代码(390MS)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; typedef long long LL; const int N=1000001; const LL mod=1000000007; int m,a[N+10]={1,1,0},b[N+10],num[N+10]; LL lcm[N+10]; LL powMod(LL a,LL b) { LL ans=1; while(b){ if(b&1) ans=ans*a%mod; a=a*a%mod; b>>=1; } return ans; } void init() { m=0; for(int i=2;i<=N;i++){ if(!a[i]){ num[m]=1; b[m++]=i; for(int j=i+i;j<=N;j+=i) a[j]=1; } } lcm[1]=1; for(int i=2;i<=N;i++){ lcm[i]=lcm[i-1]; int temp=i; if(!a[temp]){ lcm[i]=lcm[i]*temp%mod; continue; } for(int j=0;j<m&&b[j]*b[j]<=temp;j++){ int cnt=0; while(temp%b[j]==0){ cnt++; temp/=b[j]; } if(cnt>num[j]){ for(int k=1;k<=cnt-num[j];k++) lcm[i]=lcm[i]*b[j]%mod; num[j]=cnt; } } } } int main() { int T,n; init(); scanf("%d",&T); while(T--){ scanf("%d",&n); LL ans=lcm[n+1]*powMod(n+1,mod-2)%mod; printf("%I64d\n",ans); } return 0; }
|
利用结论代码(31MS)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; typedef long long LL; const int N=1000001; const LL mod=1000000007; bool a[N+10]={1,1,0}; int m,b[N+10]; LL lcm[N+10]; LL powMod(LL a,LL b) { LL ans=1; while(b){ if(b&1) ans=ans*a%mod; a=a*a%mod; b>>=1; } return ans; } void init() { memset(b,-1,sizeof(b)); for(int i=2;i<=N;i++){ if(!a[i]){ for(int j=i+i;j<=N;j+=i) a[j]=1; for(LL j=i;j<=N;j*=i) b[j]=i; } } lcm[1]=1; for(int i=2;i<=N;i++){ lcm[i]=lcm[i-1]; if(b[i]!=-1) lcm[i]=lcm[i]*b[i]%mod; } } int main() { int T,n; init(); scanf("%d",&T); while(T--){ scanf("%d",&n); LL ans=lcm[n+1]*powMod(n+1,mod-2)%mod; printf("%I64d\n",ans); } return 0; }
|
Hdu 5410 CRB and His Birthday
链接
CRB and His Birthday
题意
CRB有M块钱,要去商店买礼物,商店有N种礼品,第i种礼品单价为Wi,若买x个,
将获得Ai*x+Bi块糖果,求CRB最多可获得多少糖果?
分析
可以用背包思想来做,对于第i种礼品,可拆分成两种礼品,第一种费用为Wi,价值为Ai+Bi
只有一个,为01背包问题,第二种费用为Wi,价值为Ai,有无限个,为完全背包问题
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include<stdio.h> #include<string.h> #include<algorithm> const int N=2010; using namespace std; int m,f[N]; void zeroOne(int cost,int weight) { for(int i=m;i>=cost;i--) f[i]=max(f[i],f[i-cost]+weight); } void complete(int cost,int weight) { for(int i=cost;i<=m;i++) f[i]=max(f[i],f[i-cost]+weight); } int main() { int n,T; scanf("%d",&T); while(T--){ scanf("%d%d",&m,&n); memset(f,0,sizeof(f)); for(int i=1;i<=n;i++){ int W,A,B; scanf("%d%d%d",&W,&A,&B); zeroOne(W,A+B); complete(W,A); } printf("%d\n",f[m]); } return 0; }
|
Hdu 5414 CRB and String
链接
CRB and String
题意
给定两个字符串s,t,CRB没次可选择串s中的任意一个字符c,在其后插入任意字符d(d ≠ c),
问CRB是否能将字符串s转化为t?
分析
要由s串添加字符变为t,必须满足如下条件
1.t串的长度len(t)>=s,s串与t串相等是可以的
2.t串必须包含s序列(可以不连续即相对位置相同即可),如s=”abc”,t=”axbrcy”
3.s串和t串的第一个字母相同,且若x为s串从第一个字符开始连续相同字母的个数,
y为t串从第一个字符开始连续相同字母的个数,则x>=y
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include<stdio.h> #include<string.h> const int N=100010; char s[N],t[N]; bool judge() { int m=strlen(s); int n=strlen(t); if(m>n||s[0]!=t[0]) return false; int i=0,j=0; while(i<m&&s[i]==s[0]) i++; while(j<n&&t[j]==t[0]) j++; if(i<j) return false; i=0,j=0; while(j<n){ if(i<m&&s[i]==t[j]) i++; j++; } return i<m?false:true; } int main() { int T; scanf("%d",&T); while(T--){ scanf("%s%s",s,t); if(judge()) printf("Yes\n"); else printf("No\n"); } return 0; }
|
Hdu 5416 CRB and Tree
链接
CRB and Tree
题意
已知一个包含N个结点N-1条边的树,每一条边有一个权值,对于任何两个结点u,v(u可以等于v),f(u,v)为从u到v这条路径上边的权值的异或和,给定Q个询问,每个询问输出有多少个无序对(u,v)满足f(u,v) = s
分析
设结点u和v的最近公共祖先为fa,树的根结点为root,则f(u,v)=f(u,fa)^f(fa,v)
由于 f(fa,root)^f(root,fa)=0,
所以 f(u,v)=f(u,fa)^f(fa,root)^f(root,fa)^f(fa,v)=f(u,root)^f(root,v)
即可以用dfs求出根结点root到每一个结点的异或和,并统计不同异或和的个数,再计算答案即可
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| #include<stdio.h> #include<string.h> #include<algorithm> typedef long long LL; using namespace std; const int N=100005; int n,m,maxXor,head[N],num[N*2]; bool vis[N]; struct stu{ int to,w,next; }edge[N*2]; void addEdge(int u,int v,int w) { edge[m].to=v; edge[m].w=w; edge[m].next=head[u]; head[u]=m++; } void dfs(int pos,int sum) { num[sum]++; if(sum>maxXor) maxXor=sum; for(int i=head[pos];i!=-1;i=edge[i].next){ int v=edge[i].to,w=edge[i].w; if(!vis[v]){ vis[v]=true; dfs(v,sum^w); } } } int main() { int T; scanf("%d",&T); while(T--){ scanf("%d",&n); m=0; memset(head,-1,sizeof(head)); for(int i=1;i<n;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); addEdge(a,b,c); addEdge(b,a,c); } memset(num,0,sizeof(num)); memset(vis,false,sizeof(vis)); vis[1]=true; maxXor=0; dfs(1,0); int Q; scanf("%d",&Q); while(Q--){ int s; scanf("%d",&s); LL ans=0; if(s==0){ for(int i=0;i<=maxXor;i++) ans+=(LL)num[i]*(num[i]-1)/2+num[i]; } else{ for(int i=0;i<=maxXor;i++){ int temp=s^i; if(temp>maxXor) continue; ans+=(LL)num[i]*num[temp]; } ans>>=1; } printf("%I64d\n",ans); } } return 0; }
|