A:AOE还是单体?
排个序贪心就行了,关键是怎么实现的时候注意下细节就行了,看看是继续用AOE打所有人划得来还是直接一枪一枪打死所有人划得来。
我的写法是先统计所有人然后一步一步换算成AOE,直到不能换为止,题目数据ai范围1e9,记得开long long。
#include<bits/stdc++.h> using namespace std; typedef long long ll; inline int read(){ int s=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ s=(s<<1)+(s<<3)+(c^48);c=getchar(); } return f*s; } const int N=1555555; int a[N]; int main(){ int n=read(),x=read(); ll ans=0,s=0; for(int i=1;i<=n;i++){ a[i]=read(); ans+=a[i]; } if(x>=n){ cout<<ans<<endl; return 0; } sort(a+1,a+1+n); for(int i=1;i<=n;i++){ while(i+x<=n&&s<a[i]){ ans-=(n-x-i+1); s++; } } cout<<ans<<endl; return 0; }
B:k-size字符串
首先我们肯定要先把k个字符摆好,如果是奇数有2种情况,aba,bab,偶数的话就是abab,baba答案乘以2即可剩下的就是把剩下的a和剩下的b看看在这些a,b的位子上有多少种放法,也就是n-k/2个物品放到k/2个箱子里有多少种放法。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1555555,mod=1e9+7; ll a[N]={1},n,m,k; ll qsm(ll a,ll b){ ll s=1; while(b){ if(b%2)s=s*a%mod; a=a*a%mod;b>>=1; } return s%mod; } ll inv(ll x) { return qsm(x,mod-2); } ll C(ll n,ll m){ if(n<m||n<0||m<0)return 0; else return a[n]*inv(a[n-m])%mod*inv(a[m])%mod; } int main(){ cin>>n>>m>>k; for(int i=1;i<=max(n,m);i++){ a[i]=i*a[i-1]%mod; } if(k>n+m){ cout<<0<<endl; } else{ ll x=k>>1,y=k-x; cout<<(C(n-1,x-1)*C(m-1,y-1)%mod+C(n-1,y-1)*C(m-1,x-1)%mod)%mod<<endl; } return 0; }
C:白魔法师
看到题目提到连通块这个概念很容易想到并查集,并查集就是专门求连通块的,我们先把所有的白色合并,然后从每个黑点起手求相邻的白点的合并数量有多少个即可,每条边最多跑2次,时间复杂度O(n)。
吐槽下题目数据有点水,我同学直接暴搜都过了的,每个黑点直接开始搜旁边有多少白点。
#include<bits/stdc++.h> using namespace std; typedef long long ll; inline ll read(){ ll s=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ s=(s<<1)+(s<<3)+(c^48);c=getchar(); } return f*s; } const int N=1555555,mod=1e9+7; char s[N]; vector<int>q[N]; int a[N],num[N]; inline int find(int x){ if(a[x]!=x)a[x]=find(a[x]); return a[x]; } int main(){ int n=read(),ans=0; scanf("%s",s+1); for(int i=1;i<=n;i++)a[i]=i; for(int i=1;i<n;i++){ int x=read(),y=read(); q[x].push_back(y); q[y].push_back(x); if(s[x]==s[y]){ a[find(x)]=find(y); } } for(int i=1;i<=n;i++){ num[find(i)]++; } for(int i=1;i<=n;i++){ if(s[i]=='B'){ int sum=1; for(int j=0;j<q[i].size();j++){ if(s[q[i][j]]=='W') sum+=num[find(q[i][j])]; } ans=max(ans,sum); } } if(ans) cout<<ans<<endl; else cout<<n<<endl; return 0; }
D:抽卡
这题考点感觉就是有理数取余,概率方面怎么求并不难,不会有理数取余的同学看下https://www.luogu.com.cn/problem/P2613 洛谷有理数取余的模板题,具体证明博主也不会,这东西平时直接记下来怎么用就行了,比赛出去的时候可以把板子带上。
概率就是算减法,算所有卡池都没抽到的概率,然后用1减去这个概率就行了。
#include<bits/stdc++.h> using namespace std; typedef long long ll; inline int read(){ int s=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ s=(s<<1)+(s<<3)+(c^48);c=getchar(); } return f*s; } const int N=1555555,mod=1e9+7; ll a[N],b[N],x=1,y=1,c,d; void f(int a,int b){ if(b==0){ c=1,d=0;return ; } f(b,a%b); int lx=c; c=d,d=lx-a/b*d; } int main(){ int n=read(); for(int i=1;i<=n;i++){ a[i]=read(); } for(int i=1;i<=n;i++){ b[i]=read(); } for(int i=1;i<=n;i++){ y=y*a[i]%mod; x=(x*(a[i]-b[i]))%mod; } x=y-x; f(y,mod); x=(x%mod+mod)%mod; cout<<(x*c%mod+mod)%mod<<endl; return 0; }
E:点击消除
2个相同的字母,并且相邻碰在一起可以合并,这不就是裸的括号匹配吗,[]左括号和右括号对应,只是这里改成了字母,所以一个栈就可以解决问题了。
判断栈顶和新加的字母是不是相同,相同消去即可,最后剩下的栈是空栈就输出0.
#include<bits/stdc++.h> using namespace std; typedef long long ll; inline int read(){ int s=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ s=(s<<1)+(s<<3)+(c^48);c=getchar(); } return f*s; } const int N=1555555; char a[N],b[N]; bool vis[N]; bool check(char a[],int n){ stack<char>q; for(int i=0;i<n;i++){ if(!q.empty()&&q.top()==a[i]) q.pop(); else q.push(a[i]); } bool flag=false; int now=0; while(!q.empty()){ flag=true; b[now++]=q.top();q.pop(); } b[now]='\0'; return flag; } int main(){ scanf("%s",&a); int n=strlen(a); if(check(a,n)){ n=strlen(b); for(int i=n-1;i>=0;i--){ printf("%c",b[i]); } } else printf("0"); return 0; }
F:疯狂的自我检索者
签到题,贪心,因为剩下的人的分数是你想要多少就多少,所以尽可能高或者尽可能低即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; inline int read(){ int s=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ s=(s<<1)+(s<<3)+(c^48);c=getchar(); } return f*s; } const int N=1555555; double sum=0; int main(){ int n=read(),m=read(); for(int i=1;i<=n-m;i++){ sum+=read(); } printf("%.5lf %.5lf",(sum+m)/n,(sum+m*5)/n); return 0; }
G:解方程
很明显的二分,但是比赛时博主脑子抽风硬是想用数学方法写,然后没写出来。
log(x)以e为底的log函数在math库里面。
二分个200次以后误差应该已经很小了,不放心的话还可以再多几次,反正judge一次耗费的时间比较少。
#include<bits/stdc++.h> using namespace std; typedef long long ll; inline ll read(){ ll s=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ s=(s<<1)+(s<<3)+(c^48);c=getchar(); } return f*s; } const int N=1555555,mod=1e9+7; double ans=1; double a,b,c; bool judge(double x){ return pow(x,a)+b*log(x)<c; } int main(){ cin>>a>>b>>c; double l=0,r=1e9+7; for(int i=1;i<=200;i++){ double mid=(l+r)/2; if(judge(mid)){ l=mid; ans=l; } else{ r=mid; } } printf("%.8lf\n",ans); return 0; }
H:神奇的字母(二)
同样是签到题,用个多组输入再用个桶记录下每个字母出现多少次即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; inline int read(){ int s=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ s=(s<<1)+(s<<3)+(c^48);c=getchar(); } return f*s; } const int N=1555555; char a[N]; int vis[15555],maxn; char ans; int main(){ while(scanf("%s",&a)!=EOF){ int n=strlen(a); for(int i=0;i<n;i++){ vis[a[i]]++; if(maxn<vis[a[i]]){ maxn=vis[a[i]];ans=a[i]; } } } printf("%c",ans); return 0; }
I:十字爆破
题目是说n * m<=1e6所以估计会特意卡开2维数组的1000 * 1000的,所以我们只需要用个一维数组统计下标就行了,(i-1)*m+j表示a[i][j],再把该行该列所有值加起来的数算出来减去这个点的坐标即可,题目数据比较大,记得开long long。
#include<bits/stdc++.h> using namespace std; typedef long long ll; inline int read(){ int s=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ s=(s<<1)+(s<<3)+(c^48);c=getchar(); } return f*s; } const int N=1555555; ll h[N],l[N],a[N]; int main(){ int n=read(),m=read(); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ ll x=read(); a[(i-1)*m+j]=x; h[i]+=x; l[j]+=x; } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ printf("%lld ",h[i]+l[j]-a[(i-1)*m+j]); } printf("\n"); } return 0; }
J:异或和之和
又双叒叕是一道位运算题目,好像小白月赛每次都有这种题
这种题,不管他怎么减少的时间复杂度,首先肯定是把每个数的位数上有多少个1求出来,这个肯定是套路了。
这题是求任意3个数组成的,那么组成方式就是Cn3但是我们只需要有效的,也就是该位上出现了1个1或者3个1的情况,也就是在1的个数中取1个和0中取2个和1中取3个统计有多少种取法,再乘以该位的权值,算出来就是答案。
#include<bits/stdc++.h> using namespace std; typedef long long ll; inline ll read(){ ll s=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ s=(s<<1)+(s<<3)+(c^48);c=getchar(); } return f*s; } const int N=1555555,mod=1e9+7; int a[66]; ll ans; int main(){ int n=read(); for(int i=1;i<=n;i++){ ll x=read(); for(int j=0;j<=63;j++){ if((ll)1<<j&x){ a[j]++; } } } for(int i=0;i<64;i++){ ll x=n-a[i],y=a[i];//记得开long long不然会溢出 ans=(ans+((ll)1<<i)%mod*((y*(y-1)*(y-2)/6+x*(x-1)*y/2)%mod))%mod; } cout<<(ans+mod)%mod<<endl; return 0; }