美团CodeM初赛B轮题解

题目练习链接: https://www.nowcoder.com/test/5599304/summary

黑白树

思路

“每次操作选择的点必须是白色”这个条件其实是可以无视的,因为这可以通过自顶向 下选择来避免。
通过观察发现最底层的叶子必须选择,选完之后无视变黑的点,剩下的叶子也必须通过 选择其子树中某个节点来使自己变黑,因此贪心选择能染层数最深(最上面被染到的点最接 近根)的点+模拟即可。复杂度为线性。

参考代码

#include<bits/stdc++.h>
#define int64 long long
#define sqr(x) (x)*(x)
#define mk make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define VI vector<int>
#define VI64 vector<int64>
#define VS vector<string>
#define PII pair<int,int>
#define PDD pair<double,double>
#define VPII vector< PII >
#define SZ(x) ((int)(x).size())
#define N 120000
using namespace std;
const double pi=acos(-1);
VI E[N];
int dep[N],fa[N],ans,n,f[N],k[N],p[N];
void dfs(int x){
    f[x]=dep[x]-k[x]+1;
    p[x]=1e9;
    for(int i=0;i<E[x].size();++i){
        dep[E[x][i]]=dep[x]+1;
        dfs(E[x][i]);
        f[x]=min(f[x],f[E[x][i]]);
        p[x]=min(p[x],p[E[x][i]]);
    }
    if(p[x]>dep[x]){
        ans++;
        p[x]=f[x];
    }
}
int main(int argv,char **argc){
//    freopen("in","r",stdin);
//    freopen("out","w",stdout);
    freopen(argc[1],"r",stdin);
    freopen(argc[2],"w",stdout);
    scanf("%d",&n);
    rep(i,2,n){
        scanf("%d",&fa[i]);
        E[fa[i]].pb(i);
    }
    dep[1]=1;
    rep(i,1,n)scanf("%d",&k[i]);
    dfs(1);
    cerr<<ans<<endl;
    printf("%d\n",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

送外卖 2

思路

3^q 状压每个货物的(未领、已领、已到)状态,对于每层状态,保存现在在点 i 的最 早到达时间,每一层用 dijkstra 暴力更新最早时间 O(n^2+m)。
复杂度 O(3^q(n^2+m))
如果将图缩减为一个不超过 2q 个点的完全图(只留下订单涉及的点),两点之间的边长 为原图最短路,复杂度可以优化为 O(q(n^2+m) + 3^q
q^2)。

参考代码

#include <stdio.h>
#include <stdlib.h>
#define inf 1000000005
using namespace std;

int n,m,q,Q,i,j,k,u,v,c,ans;
int s[15],t[15],l[15],r[15],w[15],p[15];
int son[25],Next[405],ed[405],cost[405],tot;
int f[60005][25];
bool vis[25];

int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(;m;--m)
    {
        scanf("%d%d%d",&u,&v,&c);
        ++tot;Next[tot]=son[u];son[u]=tot;ed[tot]=v;cost[tot]=c;
    }
    for(i=Q=1;i<=q;++i,Q*=3)scanf("%d%d%d%d",&s[i],&t[i],&l[i],&r[i]),p[i]=Q;
    for(i=0;i<Q;++i)
    for(j=1;j<=n;++j)
    f[i][j]=inf;
    f[0][1]=0;
    for(i=0;i<Q;++i)
    {
        for(j=1;j<=n;++j)vis[j]=false;
        for(j=1;j<=n;++j)
        {
            for(u=0,k=1;k<=n;++k)if(!vis[k]&&(!u||f[i][k]<f[i][u]))u=k;
            for(k=son[u],vis[u]=true;k;k=Next[k])if(f[i][u]+cost[k]<f[i][ed[k]])f[i][ed[k]]=f[i][u]+cost[k];
        }
        for(j=1,k=i;j<=q;++j,k/=3)w[j]=k%3;
        for(v=0,j=1;j<=q;++j)
        {
            if(w[j]==0&&f[i][s[j]]<inf)
            {
                if(f[i][s[j]]>l[j])u=f[i][s[j]];else u=l[j];
                if(u<f[i+p[j]][s[j]])f[i+p[j]][s[j]]=u;
            }
            if(w[j]==1&&f[i][t[j]]<=r[j])
            {
                u=f[i][t[j]];
                if(u<f[i+p[j]][t[j]])f[i+p[j]][t[j]]=u;
            }
            if(w[j]==2)++v;
        }
        for(j=1;j<=n;++j)if(f[i][j]<inf&&v>ans)ans=v;
    }
    printf("%d\n",ans);
}

思路

图片说明

参考代码

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long LL;
int T;
LL a,b,c,k;
LL gcd(LL a,LL b)
{return b?gcd(b,a%b):a;}

int main()
{
    for(scanf("%d",&T);T--;)
    {
        scanf("%lld%lld%lld%lld",&a,&b,&c,&k);
        if(c%gcd(b,gcd(a,k-1))==0)
            puts("Yes");
        else
            puts("No");
    }
    return 0;
}

合并字符串的价值

思路

图片说明
图片说明

参考代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=500005;
int CtI[256],T;
int n,m,a[maxn],b[maxn];
char str[maxn];
int cnt[maxn][4],pos[4][maxn];

struct node
{
    int s[16];
    node *lc,*rc;

    int Max(int l,int r,const int &a,const int &b,const int &mask)
    {
        if(l>=a&&r<=b)return s[mask];
        int mid=l+r>>1;
        if(b<=mid)return lc->Max(l,mid,a,b,mask);
        if(a>mid)return rc->Max(mid+1,r,a,b,mask);
        return max(lc->Max(l,mid,a,b,mask),rc->Max(mid+1,r,a,b,mask));
    }

}ndl[maxn*2],*ns,*root;

node* build(int l,int r)
{
    node *x=++ns;
    if(l==r)
    {
        for(int mask=0;mask<16;mask++)
        {
            x->s[mask]=0;
            for(int c=0;c<4;c++)
                x->s[mask]+=(mask>>c&1)?cnt[l][c]:-cnt[l][c];
        }
    }
    else
    {
        int mid=l+r>>1;
        x->lc=build(l,mid);
        x->rc=build(mid+1,r);
        for(int mask=0;mask<16;mask++)
            x->s[mask]=max(x->lc->s[mask],x->rc->s[mask]);
    }
    return x;
}

void init()
{
    CtI['A']=0,CtI['C']=1,CtI['G']=2,CtI['T']=3; 
    scanf("%s",str+1);n=strlen(str+1);
    for(int i=1;i<=n;i++)a[i]=CtI[str[i]];
    scanf("%s",str+1);m=strlen(str+1);
    for(int i=1;i<=m;i++)b[i]=CtI[str[i]];

    for(int i=1;i<=n;i++)
    {
        memcpy(cnt[i],cnt[i-1],sizeof(cnt[0]));
        cnt[i][a[i]]++;
        pos[a[i]][cnt[i][a[i]]]=i;
    }
    ns=ndl;
    root=build(0,n);
}

void work()
{
    int bA[4],bL[4];
    memset(bA,0,sizeof(bA));
    memset(bL,0,sizeof(bL));
    for(int i=1;i<=m;i++)
        bA[b[i]]++;
    int ans=0,sl[6],cu[6];
    for(int l,r,d,k,mask,i=0;i<=m;i++)
    {
        if(i)bL[b[i]]++;
        for(int c=0;c<4;c++)
        {
            k=bA[c]-bL[c]-bL[c]+cnt[n][c];
            if(k<0)k=(k-1)/2;
            else k/=2;
            k++;
            if(k<=0)k=-1;
            else if(k>cnt[n][c])k=n;
            else k=pos[c][k]-1;
            sl[c]=cu[c]=k;
        }
        sort(sl,sl+4);
        sl[5]=n;
        for(int j=0;j<=4;j++)
        {
            if(j==0)l=0;
            else l=sl[j-1]+1;
            r=sl[j];
            l=max(l,0),r=min(r,n);
            if(l<=r)
            {
                d=mask=0;
                for(int c=0;c<4;c++)
                    if(r<=cu[c])d+=bL[c],mask|=1<<c;
                    else d+=bA[c]-bL[c]+cnt[n][c];
                ans=max(ans,d+root->Max(0,n,l,r,mask));
            }
        }
    }
    printf("%d\n",ans);
}

int main()
{
    freopen("2.in","r",stdin);
    freopen("2.out","w",stdout);
    for(scanf("%d",&T);T--;)
    {
        init();
        work();
    }
    return 0;
}

子串

思路

本题是一道简单的模拟题。直接对于 15 种可能的 k 枚举,每种情况下直接使用 KMP 算 法进行匹配即可,时间复杂度是 O(n log n + |t|), 其中|t|是 t 的长度。
可以进行一些简单的优化,例如 t 中出现数字 i 的话必然进制 k > i。这样可以常数优化。
实际上通过对 t 的结构进行研究也许可以获得更好的结果(判断断点),但是对于本题 的范围没有必要。

参考代码

#include <iostream>
#include <cassert>
#include <cstdlib>
#include <cstdio> 
using namespace std;

const int maxn = 50000 + 10;
const int maxt = 1000000 + 1;

const char alphabet[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 

char tmp[20]; 
string itoa(int n, int k) {
    int c;
    for (c = 0; n; c ++) tmp[c] = alphabet[n % k], n /= k; 
    tmp[c] = 0;
    for (int i = 0; i < c / 2; ++ i) swap(tmp[i], tmp[c-1-i]); 
    string str (tmp); 
    return str; 
}

string seq(int n, int k) {
    string str = ""; 
    for (int i = 1; i <= n; i ++)
        str = str + itoa(i, k);
    return str; 
}

string s, t; 
int nx[maxt]; 

int main() {
    int n;
    cin >> n;
    cin >> t; 
    nx[0] = 0;
    for (int i = 0; i < t.length() - 1; i ++) {
        int j = nx[i]; 
        while (j && t[i + 1] != t[j]) j = nx[j - 1];
        nx[i + 1] = (t[i + 1] == t[j]) ? j + 1 : 0; 
    }
    for (int k = 2; k <= 16; k ++)  {
        s = seq(n, k);
        int j = 0; 
        bool matched = false; 
        for (int i = 0; i < s.length(); i ++ ) {
            while (j && s[i] != t[j]) j = nx[j - 1];
            j = (s[i] == t[j]) ? j + 1 : 0; 
            if (j == t.length()) {
                matched = true;
                break; 
            }
        }
        if (matched) {
            cout << "yes\n";
            return 0; 
        }
    }
    cout << "no\n"; 
    return 0; 
}

景区路线规划

思路

图片说明

参考代码

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 105
#define eps 0.00000001
#define For(i,l,r) for(int i=l;i<=r;i++)
#define Dor(i,r,l) for(int i=r;i>=l;i--)
int n,m,K;
int pt[maxn],h1[maxn],h2[maxn];
int son[maxn],Next[maxn*maxn/2],ed[maxn*maxn/2],wt[maxn*maxn/2],cnt;
double f[60*8+5][maxn];
double Doit(int *h){
    double result=0;
    For(t,0,K) For(now,1,n) f[t][now]=0;
    For(now,1,n) f[pt[now]][now]=1.0/n;
        For(t,0,K){
            For(now,1,n) result+=f[t][now]*h[now];
            For(now,1,n){
            int num=0;

            for(int i=son[now];i;i=Next[i])
            if(K-t>=wt[i]+pt[ed[i]]) num++;

            if(num==0) continue;

            for(int i=son[now];i;i=Next[i])
            if(K-t>=wt[i]+pt[ed[i]])
                f[t+wt[i]+pt[ed[i]]][ed[i]] += f[t][now]/num;
            }
        }
        return result;
}
int main(){

    cin >> n >> m >> K;
    For(i,1,n) cin >> pt[i] >> h1[i] >> h2[i];
    For(i,1,m){
        int x, y, t;
        cin >> x >> y >> t;
        Next[++cnt]=son[x]; son[x]=cnt; ed[cnt]=y; wt[cnt]=t;
        Next[++cnt]=son[y]; son[y]=cnt; ed[cnt]=x; wt[cnt]=t;
    }
    printf("%.5lf %.5lf\n",Doit(h1),Doit(h2));
}

题目练习链接: https://www.nowcoder.com/test/5599304/summary


#美团#
全部评论

相关推荐

点赞 11 评论
分享
牛客网
牛客企业服务