zoukankan      html  css  js  c++  java
  • 6月28日考试 题解(GCD约分+动态规划+树状数组二维偏序)

    前言:考的一般般吧……T3暴力没打上来挺可惜的,到手的75分没了。

    ----------------------------------

    T1 【JZOJ4745】看电影

    Description

    听说NOIP2016大家都考得不错,于是CCF奖励省常中了 K 张变形金刚5的电影票奖励OI队的同学去看电影。可是省常中OI队的同学们共有 N(N >= K)人。于是机智的你想到了一个公平公正的方法决定哪K人去看电影。
    N个人排成一圈,按顺时针顺序标号为1 - N,每次随机一个还存活的人的编号,将这个人踢出。继续上述操作,直到剩下K个人。
    但这样显然太无聊了,于是小S又想出一个牛逼的方法。
    N个人排成一圈,按顺时针顺序标号为1 - N,每次随机一个1 - N的编号,假设随机到的编号是X,如果编号为X人还未踢出,则将这个人踢出,否则看编号为X % N + 1(即顺时针顺序下一个编号)的人是否存活,如果还未踢出则将他踢出,否则继续看编号(X + 1)% N +1的人,如果已被踢出看顺时针的下一个…………,以此类推,直到踢出一个人为止。重复上述操作,直到剩下K个人。
    已知小S的编号是Id,问按照小S的方法来他有多少的概率可以不被踢出,成功得到看电影的机会。

    Input

    第一行包括三个正整数,N,K,Id(1<=K<=N<=10^9,1<=ID<=N )

    Output

    一行一个最简分数,表示小S可以看到电影的概率。
    (如果概率为1或0,请输出1/1或0/1)

    ---------------------------

    因为在环上每个人的位置和被选中的概率是相同的。所以所有人能看电影的概率和为K。即一个人看电影的概率是K/N。约分后输出即可。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,m,k,ld;
    int gcd(int a,int b)
    {
        if(b==0)return a;
        return gcd(b,a%b);
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&ld);
        k=gcd(n,m);
        printf("%d/%d",m/k,n/k);
        return 0;
    }

    T2 【JZOJ4746】树塔狂想曲

    相信大家都在长训班学过树塔问题,题目很简单求最大化一个三角形数塔从上往下走的路径和。走的规则是:(i,j)号点只能走向(i+1,j)或者(i+1,j+1)。如下图是一个数塔,映射到该数塔上行走的规则为:从左上角的点开始,向下走或向右下走直到最底层结束。
           1
           3 8
           2 5 0
           1 4 3 8
           1 4 2 5 0
    路径最大和是1+8+5+4+4 = 22,1+8+5+3+5 = 22或者1+8+0+8+5 = 22。
    小S觉得这个问题so easy。于是他提高了点难度,他每次ban掉一个点,然后询问你不走该点的最大路径和。

    当然他上一个询问被ban掉的点过一个询问会恢复(即每次他在原图的基础上ban掉一个点,而不是永久化的修改)。

    n<=1000 m<=50W

    ----------------------------------

    看完题目后不难想到要维护最大值和次大值。我们不妨从上往下DP一次,从下往上DP一次,来维护$(i,j)$的最大值。

    写法就是$tot[i][j]=max(f[i-1][j-1],f[i-1][j])+max(ff[i+1][j],ff[i+1][j+1])+a[i][j]$

    最大值和次大值再遍历一遍就可以求出来了。

    #include<bits/stdc++.h>
    using namespace std;
    int f[1005][1005],ff[1005][1005],sum[1005],ans[1005],tot[1005][1005],second[1005];
    int n,m,a[1005][1005];
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    } 
    void dp()
    {
        f[1][1]=a[1][1];
        for (int i=1;i<=n;i++)
            for (int j=1;j<=i;j++) f[i][j]=max(f[i-1][j-1],f[i-1][j])+a[i][j];
        for (int i=n;i>=1;i--)
            for (int j=i;j>=1;j--) ff[i][j]=max(ff[i+1][j],ff[i+1][j+1])+a[i][j];
        for (int i=1;i<=n;i++)
            for (int j=1;j<=i;j++) tot[i][j]=max(f[i-1][j-1],f[i-1][j])+max(ff[i+1][j],ff[i+1][j+1])+a[i][j];
        for (int i=1;i<=n;i++)
            for (int j=1;j<=i;j++)
                if (tot[i][j]>ans[i]) ans[i]=tot[i][j],sum[i]=j;
        for (int i=1;i<=n;i++)
            for (int j=1;j<=i;j++)
                if (j!=sum[i])
                    if (tot[i][j]>second[i]) second[i]=tot[i][j];
    }
    int main()
    {
        n=read(),m=read();
        for (int i=1;i<=n;i++)
            for (int j=1;j<=i;j++) a[i][j]=read();
        dp();
        for (int i=1;i<=m;i++)
        {
            int x=read(),y=read();
            if (x==1&&y==1){
                cout<<-1<<endl;
                continue;
            }
            if (ans[x]==tot[x][y]) cout<<second[x]<<endl;
            else cout<<ans[x]<<endl;
        }
        return 0;
    }

    T3 【JZOJ4747】被粉碎的线段树

     题面

    --------------------------------

    我是没有看懂正解……贴一个正解连接吧:https://blog.csdn.net/dianning8393/article/details/101620538

    不难想到75分的做法:同样使用线段树,只不过把每个区间的mid改为他给出的mid即可。区间权值设为1。注意要使用动态开点,如果使用2倍编号类型的线段树会挂25分,因为他给出的树不是完全二叉树。

    貌似再加一些限制语句就可以到85分了,不过我没试过。

    #include<bits/stdc++.h>
    using namespace std;
    struct node
    {
        int l,r,sum,lc,rc;
    }tree[4000005];
    int n,m,a[1000005],cnt,cut[4000005],tot;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void build(int index,int l,int r)
    {
        tree[index].l=l;tree[index].r=r;
        if (l==r){
            tree[index].sum=1;
            return;
        }
        int mid=a[++cnt];cut[index]=mid;
        if (!tree[index].lc) tree[index].lc=++tot,build(tree[index].lc,l,mid);
        if (!tree[index].rc) tree[index].rc=++tot,build(tree[index].rc,mid+1,r);
        tree[index].sum=1;
    }
    inline int query(int index,int l,int r)
    {
        if (l<=tree[index].l&&tree[index].r<=r) return tree[index].sum;
        int mid=cut[index],res=0;
        if (l<=mid) res+=query(tree[index].lc,l,r);
        if (r>mid) res+=query(tree[index].rc,l,r);
        return res;
    }
    int main()
    {
        n=read(),m=read();
        for (register int i=1;i<n;i++) a[i]=read();
        tot=1;
        build(1,1,n);
        for (register int i=1;i<=m;i++)
        {
            int x=read(),y=read();
            printf("%d
    ",query(1,x,y));
        }
        return 0;
    }
  • 相关阅读:
    线程的简单介绍
    队列Joinablequeue的使用,以及生产者消费者模型的认识
    使用子线程来完成链接循环和通信循环
    使用socketserver实现简单的下载和上传
    类的绑定方法学习
    类的组合-多态-封装等特性的简单学习
    爬虫之亚马逊爬取
    JavaScript 面试中常见算法问题详解
    JavaScript 的 this 指向问题深度解析
    深入理解 JavaScript 中的函数
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13202267.html
Copyright © 2011-2022 走看看