美团CodeM复赛题解

配对游戏

思路

设f[i][j]表示前i个人中,还有j个向右看的人可以消除,这种状态的期望消除数量, g[i][j]为到达这种状态的概率。
考虑第i + 1的朝向,如果是向右,那么f[i + 1][j + 1] ← f[i][j]/2,
g[i + 1][j + 1] ← g[i][j]/2,如果是向左,那么
f[i + 1][j − 1] ← f[i][j]/2 + g[i][j],g[i + 1][j − 1] ← g[i][j]/2。(注意 j = 0的情况)

代码

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

int n,i,j,k;
double f[2005][2005],g[2005][2005],ans;

int main()
{
    scanf("%d",&n);
    g[0][0]=1;
    for(i=0;i<n;++i)
    for(j=0;j<=i;++j)
    {
        f[i+1][j+1]+=f[i][j]/2;g[i+1][j+1]+=g[i][j]/2;
        if(j)f[i+1][j-1]+=f[i][j]/2+g[i][j],g[i+1][j-1]+=g[i][j]/2;
        else f[i+1][j]+=f[i][j]/2,g[i+1][j]+=g[i][j]/2;
    }
    ans=n;
    for(i=0;i<=n;++i)ans-=f[n][i];
    printf("%.3lf\n",ans);
}

城市网络

思路

考虑离线处理,可以对每个询问,建出一个新点,作为叶子挂在起点下面。
然后建一棵新树,对于每个点,求出它的祖先中,第一个比它权值大的,作为它在新树中 的父亲。
考虑在新树上处理询问,可以倍增找出需要跳多少点。

代码

#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;

int n,q,i,j,k,u,v,l,r,ans;
int a[200005],id[200005];
int tid[200005],t[200005][20],cnt;
int tfa[200005],fa[200005],deep[200005],aim[200005];
int son[200005],Next[400005],ed[400005],tot;
bool vis[200005];

int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}
inline bool cmp(const int &x,const int &y){return a[x]<a[y];}

void dfs(int x)
{
    vis[x]=true;tid[++cnt]=x;
    for(int i=son[x];i;i=Next[i])
    if(!vis[ed[i]])
    {
        tfa[ed[i]]=x;
        deep[ed[i]]=deep[x]+1;
        dfs(ed[i]);
    }
}

int main()
{
    scanf("%d%d",&n,&q);
    for(i=1;i<=n;++i)scanf("%d",&a[i]);
    for(i=1;i<n;++i)
    {
        scanf("%d%d",&u,&v);
        ++tot;Next[tot]=son[u];son[u]=tot;ed[tot]=v;
        ++tot;Next[tot]=son[v];son[v]=tot;ed[tot]=u;
    }
    deep[1]=1;dfs(1);
    for(i=1;i<=q;++i)tid[++cnt]=n+i;
    for(i=1;i<=n+q;++i)fa[i]=id[i]=i;
    for(i=1;i<=q;++i)scanf("%d%d%d",&tfa[n+i],&aim[n+i],&a[n+i]),deep[n+i]=deep[tfa[n+i]]+1;
    sort(id+1,id+n+q+1,cmp);
    for(l=1;l<=n+q;l=r+1)
    {
        for(r=l;r<n+q&&a[id[r+1]]==a[id[l]];++r);
        for(i=l;i<=r;++i)u=id[i],fa[u]=tfa[u];
        for(i=l;i<=r;++i)u=id[i],t[u][0]=get(u);
    }
    for(i=1;i<=n+q;++i)
    {
        u=tid[i];
        for(j=0;t[u][j];++j)
        t[u][j+1]=t[t[u][j]][j];
    }
    for(i=n+1;i<=n+q;++i)
    {
        ans=0;u=i;
        for(j=18;j>=0;--j)
        if(deep[t[u][j]]>=deep[aim[i]])
        u=t[u][j],ans+=1<<j;
        printf("%d\n",ans);
    }
}

神秘代码

思路

N 个点N 条边的图一定是环套外向树。考虑将环上的所有方程都取出来(设共有x 个),那 么这些方程构成了一个x个方程x个变量且首尾相接的方程组。考虑每次用第一个方程去 消,消去相同的一部分,消完后将会得到一个仅关于第一个变量的方程。由于题目保证解 唯一,所以最后的方程是ax ≡ b (mod p)的形式,通过求a的逆元就可以求得第一个变 量的值。
求得一个值后,通过bfs递推就可以得到剩余每个位置的xi 了。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#include<cmath>
#include<string>

#define ls (t<<1)
#define rs ((t<<1)+1)
#define mid ((l+r)>>1)
#define fi first
#define se second
#define mk make_pair
#define pb push_back

#define N 300005
#define M 200005
#define seed 23333

using namespace std;
int i,j,m,n,p,k,vis[N],deg[N],Q[N],cy[N],x,y,fa[N],A[N];
long long a,b,c;
struct Node{
        long long a,b,c;
};
Node fx[N],E[N];
vector<pair<int,Node> >v[N];
void Find_Cycle()
{
        for (i=1;i<=n;++i) if (deg[i]==1) Q[++Q[0]]=i,vis[i]=1;
        int l;
        for (l=1;l<=Q[0];++l)
        {
                int p=Q[l]; 
                for (i=0;i<(int)v[p].size();++i)
                {
                        int k=v[p][i].fi; --deg[k];
                        if (!vis[k]) fa[p]=k,E[p]=v[p][i].se;
                        if (deg[k]==1)
                        {
                                Q[++Q[0]]=k; 
                                vis[k]=1;
                        }
                }
        }
        for (i=1;i<=n;++i) if (!vis[i]) break;
        for (;!vis[i];)
        {
                cy[++cy[0]]=i;
                vis[i]=1;
                for (j=0;j<(int)v[i].size();++j)
                {
                        int k=v[i][j].fi; 
                        if (!vis[k]) 
                        {
                            fx[cy[0]]=v[i][j].se;
                            i=k;
                            break;
                        }
                }
        }
}
int power(int x,int y)
{
        int sum=1;
        for (;y;y>>=1)
        {
                if (y&1) sum=1ll*sum*x%p;
                x=1ll*x*x%p;
        }
        return sum;
}
int main()
{
    scanf("%d%d",&n,&p);
    for (i=1;i<=n;++i)
    {
            scanf("%d%d%lld%lld%lld",&x,&y,&a,&b,&c);
            v[x].pb(mk(y,(Node){a,b,c}));
            v[y].pb(mk(x,(Node){b,a,c}));
            deg[x]++; deg[y]++;
    }
    Find_Cycle();
    Node now=fx[1];
    k=cy[cy[0]];
    for (i=0;i<(int)v[k].size();++i)
    {
             int p=v[k][i].fi;
             if (p==cy[1]) fx[cy[0]]=v[k][i].se;
    }
    for (i=2;i<=cy[0];++i)
    {
        now.a=(now.a*fx[i].a%p+p)%p;
        now.c=((now.c*fx[i].a-fx[i].c*now.b)%p+p)%p;
        now.b=((p-fx[i].b)*now.b%p+p)%p;
    }
    (now.a+=now.b)%=p;
    if (now.a==0) puts("cao"); else A[cy[1]]=1ll*now.c*power(now.a,p-2)%p;
    for (i=2;i<=cy[0];++i) A[cy[i]]=1ll*(fx[i-1].c-1ll*A[cy[i-1]]*fx[i-1].a%p+p)*power(fx[i-1].b,p-2)%p;
    for (i=Q[0];i;--i)
    {
            int k=Q[i];
            A[k]=1ll*(E[k].c-1ll*A[fa[k]]*E[k].b%p+p)*power(E[k].a,p-2)%p; 
    }
    for (i=1;i<=n;++i) printf("%d\n",A[i]);
}

排列

思路

图片说明

代码

#include<algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include  <stdio.h>
#include   <math.h>
#include   <time.h>
#include   <vector>
#include   <bitset>
#include    <queue>
#include      <set>
#include      <map>
using namespace std;

typedef long long LL;

const int N=100005,Mod=1e9+7;

int n,x[N],y[N],f[N],Num[N],s[N],inc[N],ans[N];

void Add(int x,int k)
{
    while(x<=n)
        s[x]=(s[x]+k)%Mod,x+=x&-x;
}

int Sum(int x)
{
    int ss=0;
    while(x)
        ss=(ss+s[x])%Mod,x-=x&-x;
    return ss;
}

int main()
{
    scanf("%d",&n);
    for(int i=1,a,b;i<=n;i++)
        scanf("%d%d",&a,&b),x[i]=a,y[a]=b;
    for(int i=n;i>=1;i--)
        Num[i]=n-i-Sum(y[i]),Add(y[i],1);
    inc[0]=inc[1]=1;
    int fac=1;
    for(int i=2;i<=n;i++)
        inc[i]=(Mod-(Mod/i)*(LL)inc[Mod%i]%Mod)%Mod,fac=fac*(LL)(i-1)%Mod;
    memset(s,0,sizeof s);
    int count=0;
    for(int i=1;i<=n;i++)
    {
        f[i]=(LL)(Sum(y[i])+1)*inc[Num[i]]%Mod;
        Add(y[i],f[i]);
        if(Num[i]==0)ans[i]=f[i]*(LL)fac%Mod,count++;
        else ans[i]=0;
    }
    for(int i=1;i<=n;++i)printf("%d\n",ans[x[i]]);
//    cout<<count<<endl;
    return 0;
}

Pairsum

思路

本题是一道趣味的构造题,构造方法不唯一,下面是作者的方法。
选取一个最大的素数p使得2p^2<=n,然后构造数列a[i] = 2ip+(i^2 mod p),其中i=1,2,…p。如果a[i] = a[j],那么有2ip=2jp从而i=j. 根据素数定理,p距离n不会太远,所以数列长度p大约是(n/2)^(1/2),远大于n^(1/2)/2。但是一些较小的n会出现问题,所以进行特判解决。
下面证明数列a的pairwise sum是distinct的。如果a[i]+a[j] = a[k]+a[l],那么考虑除以p的结果和余数,分别有2i+2j=2k+2l以及i^2+j^2==k^2+l^2 (mod p)。于是i–k =l–j且i^2-k^2 == l^2-j^2 (mod p)。显然i-k非0且mod p非0,于是得到i+k==l+j (mod p). 显然也有i-k==l-j (mod p),于是i==l (mod p),k==j (mod p),结合i,j,k,l属于{1, 2, …, p},于是有i=l,k=j。
另外,也可能有一些基于分治或者搜索的其他搜索,期待能看到一些有趣的解法。
随机方法构造的数列长度大约在n^(1/3)左右。
当我思考这个问题的时候并不知道这是一个被研究的组合问题,叫做Sidon set. 这个问题已经在2010年基本解决。实际上很多年前Paul Erdos和Pal Turan曾经证明对于n,答案最多是n^(1/2) + O(n^(1/4))。Singer有一个x^(1/2) (1-o(1))的构造。,细节可以看wikipedia。

代码

#include <iostream>
#include <cassert>
#include <stdio.h>
using namespace std;

bool isprime(int n) {
    if (n == 2) return true;
    for (int i = 2; i * i <= n; ++ i)
        if (n % i == 0) return false;
    return true; 
}

void solve(int n) {
    if (n <= 4) { cout << "1\n1" << endl; return; }
    if (n <= 16) { cout << "2\n1 2" << endl; return; }
    if (n <= 36) { cout << "3\n1 2 3" << endl; return; }
    if (n <= 64) { cout << "4\n1 2 4 8" << endl; return; }
    if (197 <= n && n <= 256) {cout << "8\n1 2 4 8 16 32 64 128" << endl; return; }

    int p = 0; 
    for (int i = 2; 2 * i * i <= n; ++ i) 
        if (isprime(i)) p = i;
    assert(4 * p * p >= n); 
    cout << p << endl; 
    for (int i = 1; i <= p; ++ i)
        cout << 2 * i * p + ((i * i) % p) << " ";
    cout << endl;
}

int main() {
    int n;
    cin >> n;
    solve(n); 
    return 0; 
}

通信

思路

图片说明
图片说明
图片说明
图片说明

http://immortalco.blog.uoj.ac/blog/2102

代码

#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <vector>
#include <bitset>
#include <string>
#include <cstdio>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
#define mk make_pair
#define pb push_back
#define fi first
#define se second
#define REP(i, x, y) for(int i = (int)x; i <= (int)y; i ++)
#define FOR(i, x, y) for(int i = (int)x; i <  (int)y; i ++)
#define PER(i, x, y) for(int i = (int)x; i >= (int)y; i --)
#define trace(x) cerr << #x << " " << x << endl;
#define dprintf(...) fprintf(stderr, __VA__ARGS__)
#define dln()        fprintf(stderr, "\n")
using namespace std;
typedef long long LL;
typedef long double db;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPI;
const    int N = 800005;
const    int P = 1e9 + 7;
const    int inf = 1e9;
const    LL Inf = 1e15;
const     int S = 1000005;

char bf[S],*p1=bf,*p2=bf;
#define nc() (p1==p2&&(p2=(p1=bf)+fread(bf,1,S,stdin),p2==p1)?-1:*p1++)
inline int IN(){
    int x=0;char ch=nc();for(;ch<'0'||ch>'9';ch=nc());
    for(;ch<='9'&&ch>='0';x=x*10+ch-48,ch=nc());return x;
}

inline int Pow(int x, int y, int p){
    int an = 1;
    for(; y; y >>= 1, x = (LL)x * x % p) if(y & 1) an = (LL)an * x % p;
    return an;
}

void renew(int &x, int y){
    x += y;
    if(x < 0) x += P;
    else if(x >= P) x -= P;
}

void setIO(string a){
#ifndef LOCAL
    freopen((a + ".in").c_str(), "r", stdin);
    freopen((a + ".out").c_str(), "w", stdout);
#else
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
#endif
}

template<typename T> inline void chkmin(T &a, const T &b) {if(a > b) a = b;}
template<typename T> inline void chkmax(T &a, const T &b) {if(a < b) a = b;}

typedef int ones[N];
typedef unsigned int u32;

bool st;
int n, top;
LL Lcost, Rcost;
ones X, Lx, Rx, stk;

struct Info{
    LL Min;
    u32 ways;
    Info() = default;
    Info(LL a, u32 b) : Min(a), ways(b) {}
};

Info operator +(const Info &a, const Info &b) {
    return (a.Min < b.Min) ? a : ((a.Min > b.Min) ? b : Info(a.Min, a.ways + b.ways));
}

Info operator +(const Info &a, const LL &b) {
    return Info(a.Min + b, a.ways);
}

Info A[16551425], B[16551425], C[16551425], D[16551425];
Info *curA = A, *curB = B, *curC = C, *curD = D;
Info dp[N];

//dp[i] = min(j < i) dp[j] + max(Ai - Ax, Bx - Bj)
//m1, x1 max = i
//m2, x2 max = j

struct segnode{
    Info *m1, *m2, *x1, *x2;
    int xl, xr, xmd;
    void init(int l, int r){
        xl = l;
        xr = r;
        xmd = (l + r) / 2;
        m1 = curA; curA += r - l + 2;
        m2 = curB; curB += r - l + 2;
        x1 = curC; curC += r - l + 2;
        x2 = curD; curD += r - l + 2;
        REP(i, l, r) x1[i - l + 1].Min = 1LL << 60;
        REP(i, l, r) x2[i - l + 1].Min = 1LL << 60;
    }
    void work(){
        REP(i, xl, xr) m1[i - xl + 1] = dp[i];
        REP(i, xl, xr) m2[i - xl + 1] = dp[i] + (-Rcost * i);
        REP(i, xmd + 2 - xl + 1, xr - xl + 1) m1[i] = m1[i - 1] + m1[i], m2[i] = m2[i - 1] + m2[i];
        PER(i, xmd - 1 - xl + 1, 1) m1[i] = m1[i + 1] + m1[i], m2[i] = m2[i + 1] + m2[i];
    }
    Info askopt1(int l, int r){
        return m1[l - xl + 1] + m1[r - xl + 1];
    }
    Info askopt2(int l, int r){
        return m2[l - xl + 1] + m2[r - xl + 1];
    }
    void renew(){
        Info d1 = Info(1LL << 60, 0), d2 = d1;
        REP(i, xl, xmd){
            d1 = d1 + x1[i - xl + 1];
            d2 = d2 + x2[i - xl + 1];
            dp[i] = dp[i] + (d1 + (Lcost * i));
            dp[i] = dp[i] + d2;
        }
        d1 = Info(1LL << 60, 0), d2 = d1;
        PER(i, xr, xmd + 1){
            d1 = d1 + x1[i - xl + 1];
            d2 = d2 + x2[i - xl + 1];
            dp[i] = dp[i] + (d1 + (Lcost * i));
            dp[i] = dp[i] + d2;
        }
    }
    void tag1(int l, int r, Info v){
//        assert(xl <= l && l <= xmd && xmd < r && r <= xr);
        l = l - xl + 1;
        r = r - xl + 1;
        x1[l] = x1[l] + v;
        x1[r] = x1[r] + v;
    }
    void tag2(int l, int r, Info v){
//        assert(xl <= l && l <= xmd && xmd < r && r <= xr);
        l = l - xl + 1;
        r = r - xl + 1;
        x2[l] = x2[l] + v;
        x2[r] = x2[r] + v;
    }
} T[2100005];
int ID[N];

#define LEADZERO(x) (__builtin_clz(x) - 2)
#define HIGH_BIT(x) (30 - LEADZERO(x))
#define findv(l, r) ((ID[l]) >> HIGH_BIT(ID[l] ^ ID[r]))

Info ask1(int l, int r){
    if(l == r) return dp[l];
    int x = findv(l, r);
    return T[x].askopt1(l, r);
}

Info ask2(int l, int r){
    if(l == r) return dp[l] + (-Rcost * l);
    int x = findv(l, r);
    return T[x].askopt2(l, r);
}

void Tag1(int l, int r, Info v){
    if(l == r){
        dp[l] = dp[l] + (v + Lcost * l);
        return;
    }
    int x = findv(l, r);
    T[x].tag1(l, r, v);
}

void Tag2(int l, int r, Info v){
    if(l == r){
        dp[l] = dp[l] + v;
        return;
    }
    int x = findv(l, r);
    T[x].tag2(l, r, v);
}

int mxp[N];
void work(int x, int L, int R){
    if(L == R) return;
    int xl = L, xr = R, xmd = (L + R) / 2;
    T[x].renew();
    work(x << 1, L, xmd);
    mxp[xmd + 1] = xmd + 1;
    REP(i, xmd + 2, xr){
        mxp[i] = mxp[i - 1];
        if(X[i] > X[mxp[i]]) mxp[i] = i;
    }
    mxp[xmd] = xmd;
    PER(i, xmd - 1, xl){
        mxp[i] = mxp[i + 1];
        if(X[i] > X[mxp[i]]) mxp[i] = i;
    }
    REP(i, xmd + 1, xr){
        LL d = Lcost * (i - mxp[i]) / Rcost, pos1 = mxp[i] - d;
        int Lt = max(Lx[mxp[i]] + 1, xl);
        pos1 = max(pos1, (LL)Lt);
        if(pos1 <= xmd) dp[i] = dp[i] + (ask1(pos1, xmd) + Lcost * (i - mxp[i]));
        pos1 = min(pos1 - 1, (LL)xmd);
        if(Lt <= pos1) dp[i] = dp[i] + (ask2(Lt, pos1) + Rcost * mxp[i]);
    }
    PER(i, xmd, xl){
        int Rt = min(Rx[mxp[i]] - 1, xr);
        LL pos1 = Rcost * (mxp[i] - i) / Lcost + mxp[i];
        pos1 = min(pos1, (LL)Rt);
        if(xmd + 1 <= pos1) Tag2(xmd + 1, pos1, dp[i] + Rcost * (mxp[i] - i));
        pos1 = max(pos1 + 1, xmd + 1LL);
        if(pos1 <= Rt) Tag1(pos1, Rt, dp[i] + (-Lcost * mxp[i]));
    }
    work(x * 2 + 1, xmd + 1, R);
    T[x].work();
}

void build(int x, int L, int R){
    if(L == R){
        ID[L] = x << LEADZERO(x);
        return;
    }
    int md = (L + R) >> 1;
    build(x << 1, L, md);
    build(x * 2 + 1, md + 1, R);
    T[x].init(L, R);
}

bool en;


int main(){

    //cerr << (&en - &st) / 1048576. << endl;
    n = IN();
    Lcost = IN();
    Rcost = IN();
    REP(i, 1, n) X[i] = IN();
    REP(i, 1, n) dp[i] = Info(1LL << 60, 0);
    dp[1] = Info(0, 1);
    REP(i, 1, n) Rx[i] = n + 1;
    REP(i, 1, n){
        while(top && X[i] > X[stk[top]]){
            Rx[stk[top]] = i;
            top --;
        }
        if(top) Lx[i] = stk[top];
        stk[++top] = i;
    }
    build(1, 1, n);
    work(1, 1, n);


    LL Ans1 = 0;
    u32 Ans2 = 0;
    REP(i, 1, n) Ans1 ^= dp[i].Min;
    REP(i, 1, n) Ans2 ^= dp[i].ways;
    cout << Ans1 << endl << Ans2 << endl;
    return 0;
}
#美团#
全部评论
第一题有O(n)的做法,见https://loj.ac/problem/6191/statistics/fastest (不过我不会)
点赞 回复 分享
发布于 2018-05-23 21:28
窝发现第一题可以不计f,直接计算g即可。统计答案就sigma(i*g[n][i]),再乘个2,反正最后剩下向左向右的人数期望是相同的。
2 回复 分享
发布于 2017-07-09 09:01
谁来解释下,第一题里面的f是概率还是期望配对的数量,不太理解第三个等式
点赞 回复 分享
发布于 2017-07-08 19:44
通信  这道题 暴力线段树 过了 95%.41  一年前的代码啊。。。。
点赞 回复 分享
发布于 2018-05-19 13:53
algo 2 我也想到了 后来实现了一个 还是超时了啊 效果反而没有我第一遍暴力的好
点赞 回复 分享
发布于 2018-05-19 14:04
请问上一届做对几道可以进复赛呀
点赞 回复 分享
发布于 2018-06-04 19:28
话说决赛试题去哪里看啊
点赞 回复 分享
发布于 2018-06-10 09:38

相关推荐

01-07 15:50
四川大学 Java
看日出看日落:好好背八股,做算法。我身边跟你bg差不多的基本都大厂暑期
点赞 评论 收藏
分享
沟头学院:无关比赛不要写,这样会显着你主次不分,比赛不要撒谎,有哪些就写那些,创新创业建议删除。技能特长可以适当夸大。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务