前面的碎碎念:
本次比赛题目比较对胃口,一个半小时就A了前五题,尤其是第二题,我正好做过它的加强版(笑)。现在比赛rating还在计算,希望能借此上个绿名吧。

题解部分:

A-打怪
签到题。当我们的攻击力大于等于怪物的总血量,每次我们都可以不耗血秒杀怪物,从而可以杀死无数只怪物,对此输出-1。当我们的攻击力小于怪物的总血量时,我们设打死一只怪物需要round个回合,而每打死一只怪物,我们将会减少(round-1)图片说明A的血量(因为最后一回合我们杀死了怪物,因而少受一回合怪物的攻击)。设答案为ans,那么我们就是要寻找满足ans图片说明(round-1)图片说明A<h最大的那个ans,对此我们二分查找这个ans即可。
时间复杂度:O(t图片说明log(h))。
代码部分:

#include<bits/stdc++.h>
using namespace std;

int main()
{
    long long T,h,a,H,A,ans,round,l,r,m;
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld%lld%lld%lld",&h,&a,&H,&A);
        if(a>=H)printf("-1\n");
        else
        {
            round=H/a+(H%a?1:0);
            for(l=0,r=1e9;l<=r;)
            {
                m=(l+r)>>1;
                if(m*(round-1)*A<h)ans=m,l=m+1;
                else r=m-1;
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}

B-吃水果
运气好做过这个题的加强版本2333,需要找规律。首先我们令ans等于n与m中较大的那个数,对于较小的那个数,使其不断乘2,直到其大于等于较大的那个数。令ans加上这个数乘二的次数,就是最终的答案。为什么呢?比如对于数字3 4,我们先减两次1,变成1 2,再使1乘2变成2,再减两次1使其同时为0。所以说,我们在减的过程中,总有机会会使较小的那个数乘2正好等于较大的那个数,最后就能同时使其为0。
时间复杂度:O(T图片说明log(n))。
代码部分:

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int i,T,n,m,ans;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        if(n>m)swap(n,m);
        ans=m;
        for(i=n;i<m;i<<=1)ans++;
        printf("%d\n",ans);
    }
    return 0;
}

C-四个选项
看到12个题目,每个题目才4个选项,很容易想到爆搜4^12种结果,加上每个选项有固定数量的限制,实际要搜索的结果数会更少。至于m个额外条件,我们先利用并查集预处理出每个题目的根节点题目,在最后统计时验证每个题目是否与其根节点题目的答案相同即可。
时间复杂度:O(12图片说明4^12)。
代码部分:

#include<bits/stdc++.h>
using namespace std;

int na,nb,nc,nd,m,ans=0,V[15],T[15];
int find(int a)
{
    if(a==V[a])return a;
    return V[a]=find(V[a]);
}
void DFS(int L,int a,int b,int c,int d)
{
    int i;
    if(L==12)
    {
        if(a!=na||b!=nb||c!=nc||d!=nd)return;
        for(i=1;i<=12;i++)if(T[i]!=T[V[i]])break;
        if(i<=12)return;
        ans++;
        return;
    }
    T[L+1]=1,DFS(L+1,a+1,b,c,d);
    T[L+1]=2,DFS(L+1,a,b+1,c,d);
    T[L+1]=3,DFS(L+1,a,b,c+1,d);
    T[L+1]=4,DFS(L+1,a,b,c,d+1);
}
int main()
{
    int i,j,k,x,y;
    scanf("%d%d%d%d%d",&na,&nb,&nc,&nd,&m);
    for(i=1;i<=12;i++)V[i]=i;
    while(m--)
    {
        scanf("%d%d",&x,&y);
        j=find(x),k=find(y);
        if(j!=k)V[j]=k;
    }
    DFS(0,0,0,0,0);
    printf("%d\n",ans);
    return 0;
}

D-最短路变短了
我们先利用堆优化的dij求出从1号节点到各点的最短距离,把所有边反向再跑一次dij,计算出从n号节点到各点的最短距离,这时我们可以得到原始情况下1号节点到n号节点的最短距离。那么对于任意一条边(u,v,w),若把边的方向反向能够使最短路变短,那么这条变短后的最短路肯定是经过了这条边的,所以这条最短路的长度肯定是1号节点到v号节点的距离+w+u号节点到n号节点的距离。我们把这个距离和原始的1号节点到n号节点的最短距离比较,若其小于原始的最短距离输出YES,否则输出NO。
时间复杂度:O(n图片说明log(n)+q)。
代码部分:

#include<bits/stdc++.h>
using namespace std;

struct node
{
    long long x,h;
    bool operator<(const node &a)const
    {
        return a.h<h;
    }
}r,f;
struct node2
{
    int x,y,w;
}E[200005];
priority_queue<node>Q;
int n,m,q;
long long ans,D[2][100005];
vector<int>R[2][100005],W[2][100005];
void dij(bool a,int sx)
{
    int i,j;
    for(i=1;i<=n;i++)D[a][i]=2e18;
    r.h=D[a][sx]=0,r.x=sx,Q.push(r);
    while(!Q.empty())
    {
        f=Q.top(),Q.pop();
        if(D[a][f.x]<f.h)continue;
        for(i=0;i<R[a][f.x].size();i++)
        {
            j=R[a][f.x][i];
            if(D[a][j]<=f.h+W[a][f.x][i])continue;
            r.h=D[a][j]=f.h+W[a][f.x][i],r.x=j,Q.push(r);
        }
    }
}
int main()
{
    int i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&E[i].x,&E[i].y,&E[i].w);
        R[0][E[i].x].push_back(E[i].y),R[1][E[i].y].push_back(E[i].x);
        W[0][E[i].x].push_back(E[i].w),W[1][E[i].y].push_back(E[i].w);
    }
    dij(0,1),dij(1,n),ans=D[0][n];
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d",&i);
        if(D[0][E[i].y]+D[1][E[i].x]+E[i].w<ans)printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

E-相似的子串
题目本质上就是要我们求,在主串中选出k个不相交且相同的子串,这个子串的最长长度是多少。很显然,这个长度越长,我们就越难在主串中选出这k个不相交且相同的子串,其具有单调性可以二分。那么我们先利用字符串哈希预处理出Hash和Power数组,接下来二分答案ans,每次判断ans是否合法,也就是判断我们能否在主串中选出k个不相交且相同的子串,并且这个子串长度为ans。
至于判断部分,我们从前往后扫描主串,利用哈希表V记录长度为len的子串的位置,用哈希表T记录长度为len的子串的个数。每次判断当前长度为len的子串,其V记录的上一个位置是否合法(也就是会不会相交),若合法更新V和T即可。若T记录的某子串个数大于等于k了,返回1表示这个长度len合法。若扫描完整个字符串也未能返回1,我们就返回0表示这个长度len非法。
时间复杂度:O(n图片说明log(n))。
代码部分:

#include<bits/stdc++.h>
using namespace std;

int n,k;
unordered_map<unsigned long long,int>V,T;
unsigned long long Hash[200005]={0},Power[200005],base=1e9+7;
char R[200005];
bool judge(int len)
{
    int i;
    unsigned long long t;
    V.clear(),T.clear();
    for(i=len;i<=n;i++)
    {
        t=Hash[i]-Hash[i-len]*Power[len];
        if(!V[t])V[t]=i,T[t]++;
        else if(V[t]<=i-len)V[t]=i,T[t]++;
        if(T[t]>=k)return 1;
    }
    return 0;
}
int main()
{
    int i,l,r,mid,ans;
    scanf("%d%d%s",&n,&k,R+1);
    for(Power[0]=i=1;i<=n;i++)
    {
        Hash[i]=Hash[i-1]*base+R[i]-'a'+1;
        Power[i]=Power[i-1]*base;
    }
    for(l=0,r=n/k;l<=r;)
    {
        mid=(l+r)>>1;
        if(judge(mid))ans=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

若有疏漏之处,还望大佬轻喷。