美团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
#美团#

查看14道真题和解析