前面的碎碎念:
这次比赛的题目感觉还是挺有趣的,可惜晚上要和朋友玩游戏,所以鸽了比赛...

正文部分:

A-牛牛爱学习
很显然,只要天数越长,所能获得的最大知识力也就越多,其具有单调递增性,因此我们可以二分这个天数,接下来的问题就变成了如何求取给定天数所能获得的最大知识力。
这里我们利用贪心的思想,先把数组从大到小排序,然后从前往后遍历。假设当前二分的天数为day,我们每day个分为一组,第1到第day个是原始值,第day+1到第2图片说明day个是原始值-1,第2图片说明day+1到第3图片说明day个是原始值-2,以此类推。注意若某本书的知识力降为0就可以停止遍历了,不然后面可能取到负的知识力,使得总知识力之和反而变小。
时间复杂度:O(n图片说明log(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(n图片说明log(n)+m图片说明log(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-n图片说明n/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(n图片说明m)。
代码部分:

#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(n图片说明log(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);
}

完结撒花,若有疏漏之处,还望大佬轻喷。