前面的碎碎念:
这次比赛的题目感觉还是挺有趣的,可惜晚上要和朋友玩游戏,所以鸽了比赛...
正文部分:
A-牛牛爱学习
很显然,只要天数越长,所能获得的最大知识力也就越多,其具有单调递增性,因此我们可以二分这个天数,接下来的问题就变成了如何求取给定天数所能获得的最大知识力。
这里我们利用贪心的思想,先把数组从大到小排序,然后从前往后遍历。假设当前二分的天数为day,我们每day个分为一组,第1到第day个是原始值,第day+1到第2day个是原始值-1,第2day+1到第3day个是原始值-2,以此类推。注意若某本书的知识力降为0就可以停止遍历了,不然后面可能取到负的知识力,使得总知识力之和反而变小。
时间复杂度:O(nlog(n))。
代码部分:
#include<bits/stdc++.h> using namespace std; int n,m,a[1000005]; bool cmp(int a,int b) { return a>b; } bool judge(int day) { long long i,s=0; for(i=0;i<n&&a[i]>i/day;i++)s+=a[i]-i/day; if(s>=m)return 1; return 0; } int main() { int i,l,r,mid,ans=-1; scanf("%d%d",&n,&m); for(i=0;i<n;i++)scanf("%d",&a[i]); sort(a,a+n,cmp); for(l=1,r=n;l<=r;) { mid=(l+r)>>1; if(judge(mid))r=mid-1,ans=mid; else l=mid+1; } printf("%d\n",ans); }
B-牛牛爱数学
我们简单的移项,发现可以把其变成一个完全平方式:(ad-bc)^2=0,也就是说ad=bc,那么接下来进行一波判断:
①当bc<0或bc>0,判断a是否与其同号。若不同号,则d肯定不能为正整数,输出-1;若同号,则继续判断bc能否整除a,若不能则输出-1,否则输出bc/a。
②当bc=0,判断a是否等于0。若a不等于0,则d只能为0,d不为正整数因而输出-1;若a等于0,则d可以取任意正整数,随便输出一个正整数即可。
时间复杂度:O(T)。
代码部分:
#include<bits/stdc++.h> using namespace std; int main() { long long T,a,b,c; scanf("%lld",&T); while(T--) { scanf("%lld%lld%lld",&a,&b,&c); if((b*c<0&&a>=0)||(b*c>0&&a<=0))printf("-1\n"); else if(b*c==0) { if(a)printf("-1\n"); else printf("1\n"); } else { if(b*c%a==0)printf("%lld\n",b*c/a); else printf("-1\n"); } } }
C-牛牛种花
我们把操作离线下来,对给定坐标数组和询问坐标数组的第一维x排序后,再对第二维y进行一个离散化处理。之后利用两个指针分别在这两个数组上移动,利用树状数组求解逆序对个数即可。
时间复杂度:O(nlog(n)+mlog(n))。
代码部分:
#include<bits/stdc++.h> using namespace std; struct node { int x,y,id; }R[100005],Q[100005]; bool cmp(node a,node b) { return a.x<b.x; } int L=1,a,ans[100005],A[200005],S[200005]={0}; int lowbit(int x) { return x&(-x); } void update(int i,int x) { while(i<=a)S[i]+=x,i+=lowbit(i); } int getsum(int i) { int s=0; while(i)s+=S[i],i-=lowbit(i); return s; } int main() { int i,j,k,n,m; scanf("%d%d",&n,&m); for(i=0;i<n;i++)scanf("%d%d",&R[i].x,&R[i].y),A[L++]=R[i].y; for(i=0;i<m;i++)scanf("%d%d",&Q[i].x,&Q[i].y),Q[i].id=i,A[L++]=Q[i].y; sort(A+1,A+L),sort(R,R+n,cmp),sort(Q,Q+m,cmp); for(i=a=2;i<L;i++)if(A[i]!=A[i-1])A[a++]=A[i]; for(i=j=0;j<m;j++) { while(i<n&&R[i].x<=Q[j].x)k=lower_bound(A+1,A+a,R[i++].y)-A,update(k,1); k=lower_bound(A+1,A+a,Q[j].y)-A,ans[Q[j].id]=getsum(k); } for(i=0;i<m;i++)printf("%d\n",ans[i]); }
D-失忆药水
题目翻译一下就是,把一个无向完全图变成一个不含奇数环的图,最少要去掉多少条边。当n为偶数,很显然每个点只能与n/2个点连边,也就是说要去掉(n-1)n/2-nn/4=n(n-2)/4条边。当n为奇数,我们在n-1个点形成的不含奇数环的图上加一个点并尽可能的连边,那么该点最多也是连(n-1)/2条边,因此n为奇数需要去掉(n-1)n/2-(n-1)(n-1)/4-(n-1)/2=(n-1)(n-1)/4条边。
时间复杂度:O(1)。
代码部分:
#include<bits/stdc++.h> using namespace std; int main() { long long n,ans; while(~scanf("%lld",&n)) { if(n&1)ans=(n-1)*(n-1)/4; else ans=(n-2)*n/4; printf("%lld\n",ans); } return 0; }
E-牛牛走迷宫
常规的BFS题,我们把方向数组按‘DLRU’的顺序设置,同时开一个pre数组记录到达某点的上一点和方向是什么。之后开始BFS,并不断更新pre数组。若不能到达终点则输出-1;若能到达终点,则输出最小步数,并利用pre数组输出路径即可。
时间复杂度:O(nm)。
代码部分:
#include<bits/stdc++.h> using namespace std; struct node { int x,y,d; }Q[250005],pre[505][505]; int r=1,f=0,dx[]={1,0,0,-1},dy[]={0,-1,1,0}; bool flag=0,V[505][505]={0}; char ans[250005],R[505][505],D[]="DLRU"; int main() { int i,n,m,x,y,d,X,Y; scanf("%d%d",&n,&m); for(i=0;i<n;i++)scanf("%s",R[i]); Q[0].x=Q[0].y=Q[0].d=0,V[0][0]=1,pre[0][0].d=-1; while(r!=f) { x=Q[f].x,y=Q[f].y,d=Q[f++].d; if(x==n-1&&y==m-1){flag=1;break;} for(i=0;i<4;i++) { X=x+dx[i],Y=y+dy[i]; if(X<0||X>=n||Y<0||Y>=m||V[X][Y]||R[X][Y]=='1')continue; pre[X][Y]={x,y,i},Q[r++]={X,Y,d+1},V[X][Y]=1; } } if(flag) { ans[d]='\0',x=n-1,y=m-1; for(i=d;i>0;x=X,y=Y)ans[--i]=D[pre[x][y].d],X=pre[x][y].x,Y=pre[x][y].y; printf("%d\n%s\n",d,ans); } else printf("-1\n"); }
F-牛牛的序列
我们知道,一个数只有加上奇数才会改变奇偶性,因此我们只需知道[1,n]区间中有多少个数字的因子和为奇数,那么就能知道[1,n]区间因子和之和的奇偶性。再利用前缀和思想,我们知道[1,l-1]区间与[1,r]区间的因子和之和的奇偶性,即可求出[l,r]区间因子和之和的奇偶性。
那么哪些数字的因子和为奇数呢。写出一些数字的因子和,我们不难发现(其实还是有点难),所有完全平方数的因子和为奇数,同时这些完全平方数乘以二,其数字的因子和也为奇数。因此,[1,n]区间因子和为奇数的数字个数即为sqrt(n)+sqrt(n/2),此处需要向下取整,同时注意sqrt函数会丢失精度。
时间复杂度:O(T)。
代码部分:
#include<bits/stdc++.h> using namespace std; bool get(long long x) { long long t1=sqrt(x)+1,t2=sqrt(x/2)+1; while(t1*t1>x)t1--; while(t2*t2>x/2)t2--; return (t1+t2)&1; } int main() { long long T,l,r; scanf("%lld",&T); while(T--) { scanf("%lld%lld",&l,&r); if(l>r)swap(l,r); printf("%d\n",get(l-1)^get(r)); } return 0; }
G-牛牛爱几何
在直接求阴影部分面积不好求的情况下,我们考虑间接求取。设圆周率为P,我们用正方形面积减去其内切圆面积,即可得到两个白色部分的面积:n^2-P(n/2)^2。之后我们用正方形面积,减去四个白色部分的面积,即可得到阴影部分的面积:n^2-2(n^2-P(n/2)^2)=(P/2-1)n^2。
时间复杂度:O(1)。
代码部分:
#include<bits/stdc++.h> using namespace std; const double P=3.14159265358979323846; int main() { int n; while(~scanf("%d",&n))printf("%.6lf\n",(P/2-1)*n*n); }
H-保卫家园
不是很懂这个题为啥会是全场AC数最少的,个人感觉这个贪心还是挺常规的(
设答案为ans,ans初值定为n-k,我们按照入伍时间从小到大排序,然后把前k个士兵的退伍时间压入multiset,之后从第k+1个士兵开始往后遍历。
若当前士兵的入伍时间小于等于multiset中保存的最小退伍时间,则无法让士兵退伍再让当前士兵入伍。此时我们比较multiset中的最大退伍时间,若当前士兵的退伍时间小于那个最大退伍时间,则把multiset中的最大退伍时间弹出,再把该士兵的退伍时间压入,相当于用当前士兵替换了那个士兵。
若当前士兵的入伍时间大于multiset中保存的最小退伍时间,则表示队伍中有人可以退伍让当前士兵入伍。我们在multiset中二分查找小于当前士兵入伍时间的最大退伍时间,把该退伍时间弹出,然后压入当前士兵的退伍时间,最后令ans--。
遍历完成后,我们输出ans即可。
时间复杂度:O(nlog(n))。
代码部分:
#include<bits/stdc++.h> using namespace std; struct node { int l,r; }R[1000005]; bool cmp(node a,node b) { return a.l<b.l; } multiset<int>T; multiset<int>:: iterator it; int main() { int i,n,k,ans; scanf("%d%d",&n,&k),ans=n-k; for(i=0;i<n;i++)scanf("%d%d",&R[i].l,&R[i].r); sort(R,R+n,cmp); for(i=0;i<k;i++)T.insert(R[i].r); for(i=k;i<n;i++) { if(R[i].l<=*T.begin()) { it=T.end(),it--; if(R[i].r<*it)T.erase(it),T.insert(R[i].r); } else it=T.lower_bound(R[i].l),it--,T.erase(it),T.insert(R[i].r),ans--; } printf("%d\n",ans); }
I-恶魔果实
恶魔果实的效果就是让a向b连一条有向边,我们建好图后,可以DFS求出从0-9这十个数字其中一个出发,能到达多少个结点,结果用num数组保存。之后分解x的每位,利用先前求得的num数组乘起来即可。
时间复杂度:O(n)。
代码部分:
#include<bits/stdc++.h> using namespace std; const int mod=1e4+7; int i,j,x,n,ans=1,num[15]={0}; bool V[15],T[15][15]={0}; void DFS(int x,int a) { for(int i=1;i<=9;i++) { if(!T[x][i]||V[i])continue; V[i]=1,num[a]++,DFS(i,a); } } int main() { scanf("%d%d",&x,&n); while(n--)scanf("%d%d",&i,&j),T[i][j]=1; for(i=0;i<=9;i++)memset(V,0,sizeof(V)),num[i]++,V[i]=1,DFS(i,i); for(;x;x/=10)ans=ans*num[x%10]%mod; printf("%d\n",ans); }
J-牛牛喜欢字符串
我们对这n/k组子串从上往下对齐放置,看每列出现次数最多的字符是什么,把该列其他不同字符都改成该字符即可。
时间复杂度:O(n)。
代码部分:
#include<bits/stdc++.h> using namespace std; char R[1000005]; int main() { int i,j,n,k,ans=0; scanf("%d%d%s",&n,&k,R); for(i=0;i<k;i++) { int V[26]={0},t=0; for(j=i;j<n;j+=k)V[R[j]-'a']++,t=max(V[R[j]-'a'],t); ans+=n/k-t; } printf("%d\n",ans); }
完结撒花,若有疏漏之处,还望大佬轻喷。