AYITOJ ROUND #1题解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013852115/article/details/83447535

自我感觉这一套题的难度梯度还是很科学的。我的预期是绝大部分人做出来简单题,少量人ac中等题。E题之前出过类似的题目,G题是一个明显的二分,如果这两道题都没思路,可以肝B题。做题情况还是不容乐观的。

分析原因可能还是大家见题比较少,尤其是新题。不能只做专题,可以经常做一下牛客网的比赛或者codeforces锻炼一下思维。

借助本次比赛修复了网站很多bug还是很好的。

就在我写这篇博客的时候居然有人用搜索过了E?并且耗时极少。一道构造题就这样变成了搜索。。。

还好有人用构造过了E。

难度分布:

简单题:DF
中等题:BEG
难题:AC

A.VN的五杀

思路:

经分析得只有敌方周围的最多5*8个点为有效点,我们可以在这最多40个点的范围内进行搜索。
可以将这些点分为5个集合,我们先得出一个攻击序列,然后每次找一个集合中的两个点,入点和出点。
可以发现入点可以取集合中距离上一个出点最近的点,并且vn在每一个集合中停留的时间一定恰好为5。
这样我们只需要在每个集合中搜索一个出点即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 1005
#define inf 0x3f3f3f3f3f3f3f3f
int dir[8][2]= {1,0,0,1,-1,0,0,-1,1,-1,-1,1,1,1,-1,-1};
int all[300][5],path[5];
ll stx,sty;
struct node
{
    ll x,y;
} p[6],e;
vector<node> vec[5];
ll ans;
ll distance(ll x1,ll y1,ll x2,ll y2)
{
    return abs(x1-x2)+abs(y1-y2);
}
void dfs(int step,ll lasty,ll lastx,ll s)
{
    if(s>ans) return;
    if(step==5)
    {
        ans=s;
        return;
    }
    //printf(">>%d %lld %lld %lld\n",step,lasty,lastx,s);
    int n=path[step];
    ll minn=inf;
    for(int i=0; i<vec[n].size(); i++)
    {
        e.y=vec[n][i].y;
        e.x=vec[n][i].x;
        if(distance(e.x,e.y,lastx,lasty)<minn)
            minn=distance(e.x,e.y,lastx,lasty);
    }
    for(int i=0; i<vec[n].size(); i++)
    {
        e=vec[n][i];
        dfs(step+1,e.y,e.x,s+minn+5);
    }
}
int main()
{
    scanf("%lld%lld",&stx,&sty);
    for(int i=0; i<5; i++)
        scanf("%lld%lld",&p[i].x,&p[i].y);
    for(int i=0; i<5; i++)
    {
        for(int j=0; j<8; j++) //找五个点周围的点
        {
            ll ty=p[i].y+dir[j][0];
            ll tx=p[i].x+dir[j][1];
            int flag=0;
            for(int k=0; k<5; k++) //判断是否重合
            {
                if(ty==p[k].y && tx==p[k].x)
                {
                    flag=1;
                    break;
                }
            }
            e.y=ty;
            e.x=tx;
            if(!flag) vec[i].push_back(e);
        }
    }
    ans=inf;
    for(int i=0; i<5; i++) path[i]=i;
    do
    {
        dfs(0,sty,stx,0);
    }while(next_permutation(path,path+5));
    printf("%lld\n",ans);
    return 0;
}

 

B.化简方程式

思路:

可以定义一个结构体表示含x的标准项。重定义运算符。用栈先处理出乘法运算,加法运算可以直接用一个数组记录x对应次幂下的系数,减法运算可以转化为加法。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 105
#define inf 0x3f3f3f3f
struct node
{
    int xi,mi;
    node(){}
    node(int xx,int mm)
    {
        xi=xx;
        mi=mm;
    }
} p;
bool cmp(node a,node b)
{
    return a.mi>b.mi;
}
int xx[10005];
char str[MAXN];
node add(node a,node b)
{
    return node(a.xi+b.xi,a.mi);
}
node mul(node a,node b)
{
    return node(a.xi*b.xi,a.mi+b.mi);
}
stack<node> st;
stack<char> fu;
int main()
{
    scanf("%s",str);
    for(int i=0; str[i]; i++)
    {
        int d=0;
        int flag=0;
        int j=i;
        if(str[i]=='-')
        {
            flag=1;
            j++;
        }
        for(j; isdigit(str[j]); j++)
        {
            d=d*10+str[j]-'0';
        }
        if(d==0) p.xi=1;
        else p.xi=d;
        if(str[j]!='x') p.mi=0;
        else
        {
            j++;
            if(str[j]!='^') p.mi=1;
            else
            {
                d=0;
                for(j=j+1; isdigit(str[j]); j++)
                {
                    d=d*10+str[j]-'0';
                }
                p.mi=d;
            }
        }
        if(flag) p.xi=-p.xi;
        //printf("%dx^%d %d\n",p.xi,p.mi,j);
        st.push(p);
        if(!fu.empty() && fu.top()=='*')
        {
            node a=st.top(); st.pop();
            node b=st.top(); st.pop();
            fu.pop();
            st.push(mul(a,b));
        }
        if(str[j]=='-') j--,fu.push('+');
        else if(str[j]=='+') fu.push('+');
        else if(str[j]=='*') fu.push('*');
        i=j;
    }
    memset(xx,0,sizeof xx);
    int maxx=0;
    while(!st.empty())
    {
        node top=st.top(); st.pop();
        xx[top.mi]+=top.xi;
        maxx=max(maxx,top.mi);
    }
    int out=0;
    for(int i=maxx;i>=1;i--)
    {
        if(xx[i]==0) continue;
        out=1;
        if(xx[i]<0)
        {
            if(xx[i]==-1) printf("-");
            else printf("%d",xx[i]);
        }
        else
        {
            if(i<maxx) printf("+");
            if(xx[i]>1) printf("%d",xx[i]);
        }
        printf("x");
        if(i>1) printf("^%d",i);
    }
    if(xx[0]!=0)
    {
        if(maxx==0 || xx[0]<0) printf("%d",xx[0]),out=1;
        else printf("+%d",xx[0]),out=1;
    }
    if(!out) printf("0");
    printf("\n");
    return 0;
}

C.取数游戏

思路:

可以用dpa[i]表示当前数为i时,轮到Alice取数时,Alice最终能取到的最大值。
dpb[i]表示当前数为i时,轮到Bob取数时,Alice最终能取到的最小值。
转移方程:
dpa[i]=max( dpb[i/k]+k , max( dpb[p] | i-m<=p<=i-1 )+p , dpb[i] ); k为i的素因子
dpb[i]=min( dpa[i/k] , min( dpa[p] | i-m<=p<=i-1 ), dpa[i]);
操作一我们可以对当前数进行分解,直接暴力枚举质因子会超时。
操作二我们可以用两个单调队列维护[i-m,i-1]区间内的最小dpa和最大dpb。
更新dpa,维护que2的时候需要考虑下标的影响,下标的影响为负。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 100005
#define inf 0x3f3f3f3f
int n,m,dpa[MAXN],dpb[MAXN],que1[MAXN],tmp1[MAXN],que2[MAXN],tmp2[MAXN];
int prime[MAXN];
bool is_prime[MAXN];
//返回n以内素数的个数
int sieve(int n)
{
    int p = 0;
    for(int i = 0; i<= n; i++) is_prime[i] = true;
    is_prime[0] = is_prime[1] = false;
    for(int i = 2; i <= n; i++)
    {
        if(is_prime[i])
        {
            prime[p++] = i;
            for(int j = 2 * i; j <= n; j += i) is_prime[j] = false;
        }
    }
    return p;
}
int main()
{
    scanf("%d%d",&n,&m);
    dpa[0]=0;
    dpb[0]=0;
    int p=sieve(n);
    int head1=1,tail1=0,head2=1,tail2=0;
    for(int i=1; i<=n; i++)
    {
        int last;
        dpb[i]=inf;
        dpa[i]=0;
        int d=i;
        for(int j=0; j<p && prime[j]<=d && prime[j]*prime[j]<=i; j++)
        {
            int num=prime[j];
            if(d%num==0)
            {
                dpb[i]=min(dpb[i],dpa[i/num]);
                dpa[i]=max(dpa[i],dpb[i/num]+num);
                while(d%num==0) d/=num;
            }
        }
        if(d>1)//压缩时间
        {
            dpb[i]=min(dpb[i],dpa[i/d]);
            dpa[i]=max(dpa[i],dpb[i/d]+d);
        }
        while(head1<=tail1 && tmp1[head1]<i-m) head1++;
        while(head1<=tail1 && que1[tail1]>=dpa[i-1]) tail1--;
        que1[++tail1]=dpa[i-1];
        tmp1[tail1]=i-1;
        dpb[i]=min(dpb[i],que1[head1]);

        while(head2<=tail2 && tmp2[head2]<i-m) head2++;
        while(head2<=tail2 && que2[tail2]<=dpb[i-1]-i+1) tail2--;
        que2[++tail2]=dpb[i-1]-i+1;
        tmp2[tail2]=i-1;
        dpa[i]=max(dpa[i],que2[head2]+i);
    }
    printf("%d\n",dpa[n]);
    return 0;
}

D.贪吃鱼

思路:

从后往前扫,维护最大值。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 100005
int n,a[MAXN];
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
        scanf("%d",a+i);
    int maxx=0,cnt=0;
    for(int i=n-1;i>=0;i--)
    {
        if(a[i]>=maxx) maxx=a[i];
        else cnt++;
    }
    printf("%d\n",n-cnt);
	return 0;
}

E.挑战数独

思路:

可以先用错位排列构造出一个全空的数独的解,然后3行3列的交换即可,可以发现一定会构造出解。

错位排列的结果可以参考maze1矩阵。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int maze1[9][9]= {{1,2,3,4,5,6,7,8,9},
    {4,5,6,7,8,9,1,2,3},
    {7,8,9,1,2,3,4,5,6},
    {2,3,4,5,6,7,8,9,1},
    {5,6,7,8,9,1,2,3,4},
    {8,9,1,2,3,4,5,6,7},
    {3,4,5,6,7,8,9,1,2},
    {6,7,8,9,1,2,3,4,5},
    {9,1,2,3,4,5,6,7,8}
};
int maze[9][9];
int x,y,d;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&y,&x,&d);
        x--;
        y--;
        memcpy(maze,maze1,sizeof maze1);
        int tt=-1;
        for(int i=0; i<3; i++)
        {
            for(int j=0; j<3; j++)
            {
                if(maze[i*3+y%3][j*3+x%3]==d)
                {
                    tt=i*3+j;
                    break;
                }
            }
            if(tt!=-1) break;
        }
        for(int i=y/3*3; i<y/3*3+3; i++)
            for(int j=0; j<9; j++)
                swap(maze[i][j],maze[tt/3*3+i-y/3*3][j]);

        for(int i=x/3*3; i<x/3*3+3; i++)
            for(int j=0; j<9; j++)
                swap(maze[j][i],maze[j][tt%3*3+i-x/3*3]);

        for(int i=0; i<9; i++)
        {
            for(int j=0; j<9; j++)
            {
                if(j) printf(" ");
                printf("%d",maze[i][j]);
            }
            printf("\n");
        }
    }
    return 0;
}

F.亵渎

思路:

判断是否连续。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 105
#define inf 0x3f3f3f3f
int n,a[MAXN];
bool vis[MAXN];
int main()
{
    scanf("%d",&n);
    memset(vis,0,sizeof vis);
    int maxx=0;
    for(int i=0;i<n;i++)
    {
        scanf("%d",a+i);
        maxx=max(maxx,a[i]);
        vis[a[i]]=1;
    }
    int flag=0;
    for(int i=1;i<=maxx;i++)
    {
        if(!vis[i])
        {
            flag=1;
            break;
        }
    }
    if(flag) puts("No");
    else puts("Yes");
    return 0;
}

G.字母

思路:

对26个字母二分最小间隔。

check时可以用vector存字母出现的位置,然后lower_bound扫一遍即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define MAXN 1000005
int n,k,ans;
char str[MAXN];
vector<int> vec[30];
int check(int len)
{
    for(int i=0;i<26;i++)
    {
        if(vec[i].size()<=k) continue;
        int last=vec[i][0];
        for(int j=1;j<k;j++)
        {
            int it=lower_bound(vec[i].begin(),vec[i].end(),last+len+1)-vec[i].begin();
            last=vec[i][it];
            if(last==inf) break;
            if(j==k-1) return 1;
        }
    }
    return 0;
}
void find(int L,int R)
{
    if(L>=R) return;
    int mid=(L+R)/2;
    if(check(mid))
    {
        ans=mid;
        find(mid+1,R);
    }
    else find(L,mid);
}
int main()
{
	scanf("%d%d",&n,&k);
	scanf("%s",str);
	for(int i=0;str[i];i++)
        vec[str[i]-'a'].push_back(i);
    for(int i=0;i<26;i++)
        vec[i].push_back(inf);
    ans=-1;
    find(0,n+1);
    printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/u013852115/article/details/83447535