• 牛客小白月赛#55


    牛客小白月赛#55

    A.至至子的等差中项

    • 题意

      • 给等差数列的a,b,以b为等差中项,输出c
    • 题解

      • 等差中项的性质
    • 代码

    #include 
    
    using namespace std;
    
    int main() {
        int a,b;
        cin>>a>>b;
        cout<<2*b-a<<'\n';
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    B.至至子的按位与

    • 题意

      • 给a,b,求一个尽可能大的c(1
    • 题解

      • 位运算是每一位独立的运算,所以一位位看规律就行。因为c尽可能大,所以尽量选1

      • abc可选c终选
        000,11
        0100
        1000
        110,11

        可以取巧,先让c中63位全为1,由表格得ab相同时c选1,不同时c选0,所以直接c-a^b

    • 代码

    #include 
    
    using namespace std;
    
    int main() {
        long long a,b;
        cin>>a>>b;
        
        long long c=(1ll<<63)-1ll;//这样写似乎不会溢出
        cout<<(c-(a^b))<<'\n';
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    C.至至子的斐波那契

    • 题意

      • 询问t次,每次询问给出x,找到斐波纳切中最接近x的下标(x<10^18)
    • 题解

      • 有结论易得,斐波纳切的88项已经超过10^18,对于每个询问要查找的范围较小,所以直接暴力即可
    • 代码

    #include 
    
    using namespace std;
    long long f[110];
    
    void solve() {
        long long x,ans;
        cin>>x;
        for(int i=1;i<=100;i++) 
            if(f[i]==x) {cout<<i<<'\n'; return ;}//恰好相等直接输出
            else if(f[i]>x) {ans=i;break;}//否则找到第一个比x大的数
        cout<<((f[ans]-x < x-f[ans-1]) ? ans:ans-1)<<'\n';//比较与前一个数列哪个更近,输出
    }
    
    int main() {
        f[1]=f[2]=1;
        for(int i=3;i<105;i++) f[i]=f[i-1]+f[i-2];//预处理出要查找的范围内的数列
        
        int t;
        cin>>t;
        while(t--) solve();
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    D.至至子的鸿门宴

    • 题意

      • 给n堆石子,满足1<=a1
      • 两人游戏,每人每轮只能拿一个石子,并且要保证上式始终成立,当不能拿的时候游戏结束
      • 至至子先手,问给定的n堆石子是谁赢
    • 题解

      • 必败态为1,2,3…n,并且拿哪一堆对于结果没有影响,所以只需要看给定的石堆离必败态有多少个石子,奇数先手必胜,否则必败
    • 代码

    #include
    
    using namespace std;
    
    int main() {
        int n;
        cin>>n;
        long long sum=0,x;
        for(int i=1;i<=n;i++) {
            cin>>x;
            sum+=x-i;
        }
        cout<<(sum&1 ? "ZZZ":"SSZ")<<'\n';
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    E.至至子的长链剖分

    • 题意

      • 给定一颗有n个点(1~n)的树,只给出每个点的高度,请你构造出符合要求的树
    • 题解

      • 用容器Pos[h]存储高度为h的节点编号是哪些
      • 根节点只有一个,所以高度最高的节点只有1个,否则无法构造
      • 高度从0到最高的h都要有点,并且高度低的点数量不能小于高度高的,否则无法构造
      • 构造方法:从底向顶链式一一对应的连边(pos[i,j]–pos[i+1,j]),若i+1高度点不够则全挂在i+1的最后一个点上
    • 代码

    #include 
    #include 
    
    using namespace std;
    
    void solve() {
        int n;
        cin>>n;
        vector<vector<int>> pos(n);
        int h=0;//注意初始化,最高的高度
        for(int i=1;i<=n;i++) {
            int t; cin>>t;
            h=max(h,t);
            pos[t].push_back(i);//高度为t的编号加上一个i
        }
        
        if(pos[h].size()>1) {cout<<"-1\n"; return ;}//根只一个
        for(int i=0;i<h;i++)
            if(pos[i].size()<pos[i+1].size()) {//底的点数量不得小于高的点数量
                cout<<"-1\n";
                return ;
            }
        
        cout<<pos[h][0]<<'\n';
        for(int i=0;i<h;i++)
            for(int j=0;j<pos[i].size();j++)//链式构造
                cout<<pos[i][j]<<' '<<pos[i+1][min(j,int(pos[i+1].size()-1))]<<'\n';
    }
    
    int main() {
        cin.tie(0)->sync_with_stdio(false);
        
        int T;
        cin>>T;
        while(T--) solve();
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    F.至至子的公司排队

    • 题意

      • n家公司,每个公司给定上下级关系,1为boss
      • 问n家公司中拓扑序的数量
    • 题解

      • 对于一家公司求拓扑序数量时:用树形dp求解

        f[u]:以u为根的子树的拓扑数
        sz[u]:以u为根的子树大小(节点数量)
        
        例如:
        将u的两颗子树v1,v2合并求f[u]:合并后的f[u]=f[v1]*f[v2]*C(v1+v2,v1)
        组合数解释:子树所有节点sz[v1]+sz[v2]进行排序,子树v1内部的拓扑数为f[v1],v2的拓扑数为f[v2],当两树节点排列时不相互插入,即前sz[v1]个数全是子树v1的节点,后sz[v2]个数全是子树v2的节点时,这种情况下sz[v1]+sz[v2]进行排序会有f[v1]*f[v2]个拓扑数,但v1,v2两子树中的节点为互不分高低的,所以两子树可以随意的互相插入,只需保持各子树内部保持拓扑序即可,所以两子树插入的方法有C(sz[v1]+sz[v2],sz[v1]),即保持sz[v1]+sz[v2]个位置中sz[v1]个数是拓扑序
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
      • 拓展到n家公司同理,只需把所有公司都看成一颗连在虚根上的子树即可,拓展后的计算公式为

      f [ u ] = ( ∏ v ∈ s o n ( u ) f [ v ] ) ⋅ ( s z [ u ] − 1 ) ! ∏ v ∈ s o m ( u ) s z [ v ] ! f [ u ] = ( s z [ u ] − 1 ) ! ⋅ ∏ v ∈ s o n ( u ) f [ v ] s z [ v ] ! f[u]=(\prod_{v\in son(u)}f[v])\cdot \frac{(sz[u]-1)!}{\prod_{v\in som(u)}sz[v]!}\\ f[u]=(sz[u]-1)!\cdot\prod_{v\in son(u)}\frac{f[v]}{sz[v]!} f[u]=(vson(u)f[v])vsom(u)sz[v]!(sz[u]1)!f[u]=(sz[u]1)!vson(u)sz[v]!f[v]

    • 代码

    #include 
    
    using namespace std;
    #define int long long
    const int N=1e5+10,mod=1e9+7;
    
    int h[N],e[N],ne[N],idx;//建树
    int f[N],sz[N];//dp数组,size数组
    int fac[N],rev[N];//排列及其逆元数组
    
    int qp(int a,int b) {//快速幂
        int res=1;
        while(b) {
            if(b&1) res=res*a%mod;
            a=a*a%mod;
            b>>=1;
        }
        return res;
    }
    
    int C(int n,int m) {//求组合数
        return fac[n]*rev[m]%mod*rev[n-m]%mod;
    }
    
    void add(int a,int b) {//加边
        e[idx]=b,ne[idx]=h[a],h[a]=idx++;
    }
    
    void dfs(int u) {//dfs树形dp
        f[u]=1,sz[u]=1;
        for(int i=h[u];~i;i=ne[i]) {
            int v=e[i];
            dfs(v);
            sz[u]+=sz[v];
            f[u]=f[u]*f[v]%mod*C(sz[u]-1,sz[v])%mod;
        }
    }
    
    int dp(int n) {//返回每一个公司的拓扑数
        idx=0;
        for(int i=1;i<=n;i++) h[i]=-1;//初始化
        for(int i=2;i<=n;i++) {//输入边
            int x;
            cin>>x;
            add(x,i);
        }
        
        dfs(1);//dfs计算
        return f[1];//f[1]即为此公司的拓扑数
    }
    
    signed main() {
        cin.tie(0)->sync_with_stdio(false);
        fac[0]=1;//预处理排列数数组
        for(int i=1;i<N;i++) fac[i]=fac[i-1]*i%mod;
        rev[N-1]=qp(fac[N-1],mod-2);
        for(int i=N-1;i>0;i--) rev[i-1]=rev[i]*i%mod;
        
        int n,m,sum=0,res=1;
        cin>>n;//n个公司,看做n个子树
        while(n--) {
            cin>>m;
            sum+=m;
            res=res*dp(m)%mod*C(sum,m)%mod;//按公式计算答案
        }
        cout<<res<<'\n';
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
  • 相关阅读:
    2022年Redis最新面试题第1篇 - Redis基础知识
    亚信科技:发挥自我优势深入AIGC,并购整合高瞻远瞩致力未来路
    数仓:为什么说 ETL 的未来不是 ELT,而是 EL (T)
    【自动驾驶地图】OpenDrive协议总结(上)
    DALLE·2(Hierarchical Text-Conditional Image Generation with CLIP Latents)
    【信号处理】卡尔曼(Kalman)滤波(Matlab代码实现)
    npm命令--安装依赖包--用法/详解
    Deepfake!黑客冒充非洲联盟主席与多位欧洲领导人通话
    分布式事务-TCC案例分析流程图
    2020java面试总结
  • 原文地址:https://blog.csdn.net/m0_49843646/article/details/126465828