前面的碎碎念:
啊啊啊,差点就AK了好可惜啊,数学题忘了小球放盒子怎么快速求方法数了,对不起高中数学老师...
题解部分:
A-AOE还是单体?
比较简单的贪心题目。若怪兽数目大于x,那么释放一个AOE技能对所有怪物造成1伤害,肯定比一个一个放个单体技能对所有怪物造成1伤害赚;若怪兽数目等于x,两者等价;若怪兽数目小于x,单体技能比AOE技能更赚。所以我们先用AOE技能把怪杀到只剩x-1个,再用单体技能把剩下的怪物全部杀死即可。
时间复杂度:O(nlog(n))。
代码部分:
#include<bits/stdc++.h> using namespace std; int a[200005]; int main() { int i,n,x; long long ans=0; scanf("%d%d",&n,&x); for(i=1;i<=n;i++)scanf("%d",&a[i]); sort(a+1,a+1+n); if(n<=x)for(i=1;i<=n;i++)ans+=a[i]; else { ans=x*a[n-x]; for(i=n-x+1;i<=n;i++)ans+=a[i]-a[n-x]; } printf("%lld\n",ans); return 0; }
B-k-size字符串
我们先按照abab...或者baba...这样排成k个,然后把剩下的a字符和b字符再插入到排好的a字符和b字符的位置。假设排成k个的字符中有m个a字符,剩下了n个a字符,那么插入的问题就等价于n个相同的小球放入m个盒子,允许盒子为空,这个方法数是C(n+m-1,m-1)。同理可以求出b字符的方法数,ab两字符的方法数相乘,加到ans上即可。而组合数可以通过预处理阶乘和阶乘的逆元,之后直接快速求解。
时间复杂度:O(max(n,m))。
代码部分:
#include<bits/stdc++.h> using namespace std; const int mod=1e9+7; long long fac[100005],inv[100005]; int fastpow(long long a,int b) { long long s=1; for(;b;b>>=1,a=a*a%mod)if(b&1)s=s*a%mod; return s; } int Cal(int m,int n) { return fac[m]*inv[n]%mod*inv[m-n]%mod; } int main() { //A和B分别表示一开始k个字符中,'a'字符和'b'字符的个数 //a和b分别表示剩余的'a'字符和'b'字符的个数 int n,m,k,A,B,a,b; long long i,j,ans=0; scanf("%d%d%d",&n,&m,&k); for(fac[0]=i=1;i<=1e5;i++)fac[i]=fac[i-1]*i%mod; inv[100000]=fastpow(fac[100000],mod-2); for(i=99999;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod; A=k/2,B=k/2+k%2,a=n-A,b=m-B; //剩余的'a'字符和'b'字符个数应该非负,否则就是非法的 if(a>=0&&b>=0) { i=Cal(a+A-1,A-1),j=Cal(b+B-1,B-1); ans=(ans+i*j%mod)%mod; } A=k/2+k%2,B=k/2,a=n-A,b=m-B; if(a>=0&&b>=0) { i=Cal(a+A-1,A-1),j=Cal(b+B-1,B-1); ans=(ans+i*j%mod)%mod; } printf("%lld\n",ans); return 0; }
C-白魔法师
我们先利用并查集,DFS求出所有白色连通块及其对应的结点个数,将ans初始化为最大的白色连通块的结点个数。之后枚举黑色结点,将所有和它直接相连的白色连通块的节点个数全部加起来,设其为s,则令ans=max(s+1,ans),最后输出ans即可。
时间复杂度:O(n)。
代码部分:
#include<bits/stdc++.h> using namespace std; int ans=0,V[100005],S[100005]={0}; char T[100005]; int find(int a) { if(V[a]==a)return a; return V[a]=find(V[a]); } vector<int>R[100005]; void DFS(int x,int fa) { int i,j,a,b; for(i=0;i<R[x].size();i++) { j=R[x][i]; if(j==fa)continue; if(T[j]=='W'&&T[x]=='W') { a=find(x),b=find(j); if(a!=b)V[b]=a,S[a]+=S[b],S[b]=0,ans=max(ans,S[a]); } DFS(j,x); } } int main() { int i,j,k,s,n; scanf("%d%s",&n,T+1); for(i=1;i<=n;i++) { V[i]=i; if(T[i]=='W')S[i]=1; } for(i=1;i<=n-1;i++)scanf("%d%d",&j,&k),R[j].push_back(k),R[k].push_back(j); DFS(1,0); for(i=1;i<=n;i++) { if(T[i]=='W')continue; for(s=j=0;j<R[i].size();j++) { k=R[i][j]; if(T[k]=='W')s+=S[find(k)]; } ans=max(ans,s+1); } printf("%d\n",ans); return 0; }
D-抽卡
简单的数学题,抽到自己想要的卡的概率=1-每个卡池都抽不到自己想要的卡的概率之积,即ans=1-[(a1-b1)/a1][(a2-b2)/a2]...[(an-bn)/an]。
时间复杂度:O(nlog(n))。
代码部分:
#include<bits/stdc++.h> using namespace std; const int mod=1e9+7; int i,n,A[100005],B[100005]; int fastpow(long long a,int b) { long long s=1; for(;b;b>>=1,a=a*a%mod)if(b&1)s=s*a%mod; return s; } int main() { long long ans=1; scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d",&A[i]),ans=ans*fastpow(A[i],mod-2)%mod; for(i=1;i<=n;i++)scanf("%d",&B[i]),ans=ans*(A[i]-B[i])%mod; printf("%lld\n",(1-ans+mod)%mod); return 0; }
E-点击消除
栈的简单应用。从左到右遍历一遍字符串,若栈为空,直接把字符入栈;若栈不为空,判断栈顶元素和当前字符是否一样,若一样栈顶元素出栈,若不一样把该字符入栈。最后判断栈是否为空,若为空输出0,否则从栈底到栈顶输出栈内剩余元素。
时间复杂度:O(n)。
代码部分:
#include<bits/stdc++.h> using namespace std; char R[300005],S[300005]; int main() { int i,t=-1; scanf("%s",R); for(i=0;R[i]!='\0';i++) { if(t==-1)S[++t]=R[i]; else { if(R[i]==S[t])t--; else S[++t]=R[i]; } } if(t==-1)printf("0\n"); else S[++t]='\0',printf("%s\n",S); return 0; }
F-疯狂的自我检索者
签到题。先统计未被隐藏的分数之和,对于被隐藏的分数,全部令其为最大值5,加起来再除以打分人数,即可得到最大可能平均分;全部令其为最小值1,加起来再除以打分人数,即可得到最小可能平均分。
时间复杂度:O(n)。
代码部分:
#include<bits/stdc++.h> using namespace std; int main() { int i,j,n,m; double ans1=0,ans2=0; scanf("%d%d",&n,&m); for(i=1;i<=n-m;i++) { scanf("%d",&j); ans1+=j,ans2+=j; } while(m--)ans1+=1,ans2+=5; printf("%.5lf %.5lf\n",ans1/n,ans2/n); return 0; }
G-解方程
设f(x)=x^a+bln(x),很显然f(x)是个单调递增函数,对于最小的c=1,总有x=1能使其成立,故这个方程不存在无解的情况。那么我们可以利用三分法来找到这个x,注意设置一个计数变量防止无限循环卡死的情况。
时间复杂度:O(log(c))。
代码部分:
#include<bits/stdc++.h> using namespace std; int t=0,a,b,c; double F(double x) { return pow(x,a)+b*log(x); } int main() { double i,j,l=1,r=1e9; scanf("%d%d%d",&a,&b,&c); while(r-l>0.0000001) { t++,i=(l+r)/2,j=(i+r)/2; if(t>1000)break; if(F(i)<c)l=i; else r=j; } printf("%.14lf\n",i); return 0; }
H-神奇的字母(二)
签到题。直接利用读到文件尾的while循环统计各个字母的出现个数,最后再把出现次数最多的字母输出即可。
时间复杂度:O(n)。
代码部分:
#include<bits/stdc++.h> using namespace std; int main() { int i,j,ans,V[26]={0}; char c; while(~scanf("%c",&c))if(c>='a'&&c<='z')V[c-'a']++; for(i=j=0;i<26;i++)if(V[i]>j)ans=i,j=V[i]; printf("%c\n",ans+'a'); return 0; }
I-十字爆破
设原数组为R,H[i]为第i行数字之和,L[j]为第j列数字之和,那么ans[i][j]=H[i]+L[j]-R[i][j]。至于R数组无法直接开出来的情况,我们可以利用vector容器动态分配内存,当然把二维数组压缩成一维数组也行。
时间复杂度:O(nm)。
代码部分:
#include<bits/stdc++.h> using namespace std; vector<long long>R[1000005]; long long H[1000005]={0},L[1000005]={0}; int main() { int i,j,k,n,m; scanf("%d%d",&n,&m); for(i=0;i<n;i++) for(j=0;j<m;j++) { scanf("%d",&k); R[i].push_back(k); H[i]+=k,L[j]+=k; } for(i=0;i<n;i++) { for(j=0;j<m;j++)printf("%lld ",H[i]+L[j]-R[i][j]); printf("\n"); } return 0; }
J-异或和之和
经典按位考虑算贡献。从左到右遍历一遍,若ai的第j位是1,若要该位有贡献,那么只能是100或者111的情况,设从i+1到n,第j位是1的个数为one,第j位是0的个数为zero,那么贡献=(1<<j)C(one,2)+(1<<j)C(zero,2);若ai的第j位是0,若要该位有贡献,那么只能是010或者001的情况,设从i+1到n,第j位是1的个数为one,第j位是0的个数为zero,那么贡献=(1<<j)onezero,以上的C(m,n)是组合数。至于快速计算i+1到n,第j位0与1的个数,我们可以利用前缀和数组预处理出来。
时间复杂度:O(nlog(max(ai)))。
代码部分:
#include<bits/stdc++.h> using namespace std; const int mod=1e9+7; long long a[200005]; int S[60][200005]={0}; int main() { int i,j,n,s0,s1,ans=0; __int128 one=1; scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%lld",&a[i]); for(j=0;j<60;j++) { S[j][i]=S[j][i-1]; if((one<<j)&a[i])S[j][i]++; } } for(i=1;i<n-1;i++) { for(j=0;j<60;j++) { s1=S[j][n]-S[j][i],s0=n-i-s1; if((one<<j)&a[i]) { ans=(ans+(one<<j)*s1*(s1-1)/2)%mod; ans=(ans+(one<<j)*s0*(s0-1)/2)%mod; } else ans=(ans+(one<<j)*s1*s0)%mod; } } printf("%d\n",ans); return 0; }
完结撒花,若有不足之处,还望大佬轻喷~