1. 程式人生 > >Nowcoder 牛客練習賽23

Nowcoder 牛客練習賽23

選中 大二 兩個 兩種 遊戲 sta 過去 有關 emc

Preface

終於知道YKH他們為什麽那麽喜歡打牛客網了原來可以抽衣服

那天晚上有空就也去玩了下,刷了一波水TM的YKH就抽到了,我當然是沒有

題目偏水,好像都是1A的。才打了一個半小時,回家就直接睡覺了


A 托米的賭球

送分題,考慮貪心的思想,由於要總數量最小,因此面額大的應該能選就選

所以一路貪心下來即可。 CODE

#include<cstdio>
#include<cctype>
using namespace std;
const int A[7]={100,50,20,10,5,2,1},B[6]={50,20,10,5,2,1};
int n,a,b,p;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(int x)
{
    if (x>9) write(x/10);
    putchar(x%10+'0');
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i; read(n);
    while (n--)
    {
        read(a); read(b); p=0;
        for (i=0;i<7;++i) write(a/A[i]),putchar(' '),a%=A[i];
        for (i=0;i<5;++i) write(b/B[i]),putchar(' '),b%=B[i]; write(b/B[5]); putchar('\n');
    }
    return 0;
}

B 托米的劃分

首先我們發現一個十分顯然的性質,每次劃分時的兩個數應當盡可能接近,這樣乘積才會最小。

可以結合長方形周長確定,兩邊越接近面積越小來理解當然用二次函數證明之也可

然後考慮直接遞歸計算這個過程,這樣貌似加上記憶化都是\(O(n)\)的。

隨即我們發現這個可以打表,但是同樣\(O(n)\)的時空復雜度難以接受。

然後我們就要發揮一下亂搞的技巧了,我們把\(10^6\)以內的數打表出來,然後剩下的直接遞歸處理即可。

這樣遞歸層數很小足以通過。 CODE

#include<cstdio>
#include<cctype>
using namespace std;
const int N=1e6;
int t,n; long long d[N+5];
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(long long x)
{
    if (x>9) write(x/10);
    putchar(x%10+'0');
}
inline void init(void)
{
    for (register int i=1;i<=N;++i)
    d[i]=d[i+1>>1]+d[i>>1]+1LL*(i+1>>1)*(i>>1);
}
inline long long solve(int x)
{
    if (x<=N) return d[x];
    return solve(x+1>>1)+solve(x>>1)+1LL*(x+1>>1)*(x>>1);
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i; read(t); init();
    while (t--)
    {
        read(n); write(solve(n)); putchar('\n');
    }
    return 0;
}

C 托米的位運算

考慮轉化問題,當給價最大化是,就是當它們的共同二進制位最大時就是最大給價。

所以貪心地從大到小枚舉二進制位,滿足以下條件即可:

  • \(a_i\)最大二進制位為\(1\)
  • 滿足上一條的\(a_i\)\(\operatorname{and}\)所得剩下的公有二進制位不小於目前的最高位

然後就輕松水過了。 CODE

#include<cstdio>
#include<cctype>
using namespace std;
typedef long long LL;
const LL N=1e5+5;
LL t,n,a[N],b[N],cnt,tot;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(LL &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(LL x)
{
    if (x>9) write(x/10);
    putchar(x%10+'0');
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register LL i,j; read(n);
    for (i=1;i<=n;++i) read(a[i]);
    for (cnt=0,tot=(1LL<<31)-1,j=30;j>=0;--j)
    {
        for (cnt=0,i=1;i<=n;++i)
        if (a[i]&(1LL<<j)) b[++cnt]=a[i],tot&=a[i];
        if (tot%(1LL<<j)==0)
        {
            for (printf("%lld\n",cnt),i=1;i<cnt;++i)
            printf("%lld ",b[i]); printf("%lld",b[cnt]);
            break;
        }
    }
    return 0;
}

D 托米的咒語

本來可能還想騙我們寫寫DFS增加下碼量的,然後我用next_premutation水過去了

數據範圍這麽小,因此全排列來一發,考慮怎麽判斷

我們對於原來的字符串處理兩個數組。\(f_{i,j}\)表示第\(i\)之後(不包括\(i\)最近的一個\(j\)字符的位置,\(fir_i\)表示字符\(i\)在串中最早出現的位置。

判斷時直接\(O(9)\)向後跳即可,CODE

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3005,P=15;
char s[N]; bool flag;
int f[N][P],num[P],fir[P],ans,n,now;
int main()
{
    register int i; scanf("%s",s+1); n=strlen(s+1);
    memset(f,63,sizeof(f));
    for (i=n-1;i>=1;--i)
    {
        memcpy(f[i],f[i+1],sizeof(f[i])); f[i][s[i+1]-'a'+1]=i+1;
        fir[s[i]-'a'+1]=i;
    }
    for (i=1;i<=9;++i) num[i]=i;
    do
    {
        for (now=fir[num[1]],flag=1,i=2;i<=9;++i)
        if (f[now][num[i]]<=n) now=f[now][num[i]]; else { flag=0; break; }
        ans+=flag;
    }while (next_permutation(num+1,num+10));
    return printf("%d",ans),0;
}

E 托米的數學

貌似是不可食用的數學題不會留著以後填坑


F托米的遊戲

這題如果你光用腦子想可能會感到毫無頭緒,但是如果拿起筆寫一下式子就會發現這是個SB題。

考慮利用期望的線性性質來分析,總的期望輪數\(E\)等於各點的期望輪數\(e_i\)之和。

我們先DFS求出每個點的深度\(dep_i(dep_{rt}=1)\),那麽對於可能的情況只有兩種:

  1. 選中該點導致該點被砍去,那麽此時的概率\(\frac{1}{dep_x}\)(因為刪去點的過程只與這個點的祖先個數(即深度)有關),期望貢獻\(1\),則期望為\(\frac{1}{dep_x}\)
  2. 選到該點的祖先導致該點被砍去,此時概率都不用算,因為期望貢獻\(0\),乘一下就是\(0\)

因此答案就是\(\sum_{i=1}^n \frac{1}{dep_i}\)

CODE

#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
const int N=100005,mod=998244353;
struct edge
{
    int to,next;
}e[N<<1];
int n,dep[N]={0,1},head[N],cnt,x,y,ans;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void double_add(int x,int y)
{
    e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
    e[++cnt].to=x; e[cnt].next=head[y]; head[y]=cnt;
}
inline void inc(int &x,int y)
{
    if ((x+=y)>=mod) x-=mod;
}
inline int quick_pow(int x,int p)
{
    int tot=1;
    for (;p;x=1LL*x*x%mod,p>>=1) if (p&1) tot=1LL*tot*x%mod;
    return tot;
}
inline void DFS(int now,int fa)
{
    inc(ans,quick_pow(dep[now],mod-2));
    for (register int i=head[now];~i;i=e[i].next)
    if (e[i].to!=fa) dep[e[i].to]=dep[now]+1,DFS(e[i].to,now);
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i; read(n); memset(head,-1,sizeof(head));
    for (i=1;i<n;++i) read(x),read(y),double_add(x,y);
    return DFS(1,-1),printf("%d",ans),0;
}

Nowcoder 牛客練習賽23