• 沈阳集训day4


    问题 A: 借书

    题目描述

    Dilhao一共有n本教科书,每本教科书都有一个难度值,他每次出题的时候都会从其中挑两本教科书作为借鉴,如果这两本书的难度相差越大,Dilhao出的题就会越复杂,也就是说,一道题的复杂程度等于两本书难度差的绝对值。
    这次轮到ldxxx出题啦,他想要管Dilhao借m本书作为参考去出题,Dilhao想知道,如果ldxxx在Dilhao给出的m本书里挑选难度相差最小的两本书出题,那么ldxxx出的题复杂程度最大是多少?

    输入

     第一行是n和m。
     接下来的n行,每行一个整数ai表示第i本书的难度。

    输出

     一个整数为ldxxx出的题复杂程度的最大值。

    样例输入

    6 3 
    5
    7
    1
    17
    13
    10

    样例输出

    7

    提示


    【样例解释】


    Dilhao给了ldxxx难度为1,10,17的三本书,ldxxx挑选难度为10和17的两本书,出题复杂度为7;

    如果Dilhao给出其他任何三本书,其中的两本书难度差的最小值都小于7,所以ldxxx出题的最大复杂程度为7。

    【数据说明】 

    对于 30%的数据: 2<=n<=20; 

    对于 60%的数据: 2<=n<=1000; 

    对于 100%的数据: 2<=n<=100000, 2<=m<=n, 0<=ai<=1000000000。
    题解:二分
    #include <bits/stdc++.h>
     
    using namespace std;
    const int maxn = 1e5+5;
    int a[maxn], n, m;
     
    bool check(int k){
        int now = a[1], cnt = 1, pos1 = 1;
        while(cnt < m){
            int nxt = now+k;
            int pos = lower_bound(a+pos1, a+1+n+1, nxt) - a;
            if(pos == n+1)return 0;
            cnt++;
            now = a[pos];
            pos1 = pos;
        }
        return 1;
    }
     
     
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++)scanf("%d", &a[i]);
        sort(a+1, a+1+n);
        a[n+1] = 2*a[n];
        int lf = 0, rg = a[n], ans;
        while(lf <= rg){
            int mid = (lf + rg) >> 1;
            if(check(mid))ans = mid, lf = mid + 1;
            else rg = mid - 1;
        }
        printf("%d
    ", ans);
        return 0;
    }
    View Code

    问题 B: 追捕

    题目描述

    Duan2baka:“jmsyzsfq天下第一蠢!”
    jmsyzsfq:“你说什么?!”
     
     
    于是Duan2baka开始了逃亡的旅程,而jmsyzsfq也开始追捕Duan2baka。 他们来到了一个有n个节点的有向图,迅速逃跑的Duan2baka会首先降落在有向图的某个节点T上,因为怕发出声音,他会永远静止不动。而随后跟来的jmsyzsfq会降落在图上随机一个节点S上(可以与T相同),并可以沿着图中的有向边随意走动。因为jmsyzsfq有着敏锐的嗅觉而且绝顶聪明,他一定会沿着正确的方向寻找Duan2baka。你可以认为,如果图中存在一条合法的从S到T的路径,那么jmsyzsfq一定会抓到Duan2baka——因此jmsyzsfq能否追捕到Duan2baka在他刚刚降落在图上的时候就已经确定了。现在请你帮帮jmsyzsfq,在他降落前告诉他有多大的概率能够追捕到Duan2baka,并告诉他想要追到Duan2baka他可以降落在哪些节点上。

    输入

    输入第一行包含三个整数n,m,opt,表示图中有n个节点,m条有向边;opt为0或1,含义见输出格式所述。
        输入第2至m+1行每行两个整数u,v描述了图中一条从u到v的有向边。
    输入第m+2行一个整数T表示Duan2baka降落的位置。

    输出

      如果输入的opt为0,只需要输出jmsyzsfq能够追捕到Duan2baka的概率。
      如果输入的opt为1,输出两行,第一行输出jmsyzsfq能够追捕到Duan2baka的概率;第二行按从小到大输出想要追到Duan2baka他可以降落的节点编号,编号间用空格隔开。
      对于jmsyzsfq能够追捕到Duan2baka的概率,是一个既约分数,形如“a/b”(a,b为整数),使gcd(a,b)=1,如果a=b,输出1/1。

    样例输入

    【样例1输入】
    9 10 1
    1 2
    2 1
    2 4
    6 1
    9 6
    6 5
    5 3
    3 7
    3 1
    1 8
    1
    【样例2输入】
    5 7 1
    1 2
    1 3
    1 5
    2 4
    4 1
    4 5
    5 3
    1

    样例输出

    【样例1输出】
     2/3
     1 2 3 5 6 9 
    【样例1解释】
     图中共9个节点,能够到达节点S=1的节点共6个:{1,2,3,5,6,9}。所以能够追捕到Duan2baka的概率为6/9=2/3
    
    【样例2输出】
     3/5
     1 2 4 

    提示

      opt=0和opt=1的数据各50%。

    对于opt=0和opt=1的情况,有着完全相同的子任务:

    对于10%的数据,n,m≤100。

    对于另外30%的数据,n,m≤100,000。

    对于另外10%的数据,保证图是一个完全图,即对于任意两个点u和v,必然有两条有向边u→v和v→u。

    对于另外20%的数据,保证图是一个有向无环图,即对于任意一个点x,不存在图上一条路径从x经过其它点后回到x。

    对于另外20%的数据,保证如果图上存在一条边u→v,一定存在一条边v→u。

    对于100%的数据,n,m≤1,000,000,保证数据合法且不存在重边、自环。
    题解:简单bfs
    #include <bits/stdc++.h>
     
    using namespace std;
    const int maxn = 1e6+5;
    int n, m, opt, st, cnt = 1, tot, h[maxn];
    bool vis[maxn];
    int gcd(int a, int b){
        return b ? gcd(b, a%b) : a;
    }
    struct edge{int v,nxt;}G[maxn];
    void add(int u, int v){
        G[++tot].v = v; G[tot].nxt = h[u]; h[u] = tot;
    }
    void bfs(){
        queue <int> Q;
     
        Q.push(st);
        vis[st] = 1;
        while(!Q.empty()){
            int u = Q.front(); Q.pop();
     
            for(int i = h[u]; i; i = G[i].nxt){
                int v = G[i].v;
                if(!vis[v]){
                    Q.push(v);
                    vis[v] = 1;
                    cnt++;
                }
            }
        }
     
    }
    int main()
    {
        scanf("%d%d%d", &n, &m, &opt);
        for(int i = 1; i <= m; i++){
            int u, v;
            scanf("%d%d", &u, &v);
            add(v, u);
        }
        scanf("%d", &st);
        bfs();
        int d = gcd(cnt, n);
        int p = cnt/d, q = n/d;
        printf("%d/%d
    ",p,q);
        if(opt)for(int i = 1; i <= n; i++)
           if(vis[i])printf("%d ",i);
    }
    View Code

    问题 C: 空间活动

    题目描述

       贝茜和佩奇正在玩一款游戏,在游戏开始会生成一个有n个点m条单向边的地图,经过每条边需要花费价格为Hi的费用(Hi<=1000)。但是如果两个点可以互相到达,那么这两个点之间需要花费的价格就变为0。总共有k关,每一关会给你两个点a,b(0< a,b <=n),让你求从a走到b的最小花费。

    输入

       第一行三个数n,m,k;
       接下来m行每行三个数,x,y,Hi表示存在一条(x,y)的单向边,边权为Hi;
       接下来k行每行两个数a,b。

    输出

     k行,每行一个数表示从a走到b的最小花费(如果不能从a走到b,那么输出“No”)。

    样例输入

    4 5 5
    1 2 5
    2 1 10
    3 4 8
    4 3 7
    2 3 6
    1 2
    1 3
    1 4
    4 3
    4 1

    样例输出

    0
    6
    6
    0
    No

    提示

       对于30%的数据n<=20,k<=1;

       对于另外30%的数据 n<=100,m=n-1,k<=50;

       对于100%的数据 n<=1000,m<=2500,k<=100;
    题解:tarjan裸题
    #include <bits/stdc++.h>
     
    using namespace std;
    const int maxn = 3000+5;
    #define inf 1000000008
    int n, m, st, ed, k, tot, h[maxn], hh[maxn], tot1, dis[maxn], ans[maxn*3];
    bool vis[maxn], run[maxn], inq[maxn], ins[maxn];
    struct edge{int v, nxt, w;}G[maxn], g[maxn];
    void add(int u, int v, int w){
        G[++tot].v = v; G[tot].nxt = h[u]; h[u] = tot; G[tot].w = w;
    }
    void add1(int u, int v, int w){
        g[++tot1].v = v; g[tot1].nxt = hh[u]; hh[u] = tot1; g[tot1].w = w;
    }
    bool spfa(){
        queue <int> Q;
        memset(dis, 127, sizeof(dis));
        memset(inq, 0, sizeof(inq));
        Q.push(st);
        inq[st] = 1;
        dis[st] = 0;
        while(!Q.empty()){
            int u = Q.front(); Q.pop();inq[u] = 0;
     
            for(int i = h[u]; i; i = G[i].nxt){
                int v = G[i].v;
                if(dis[v] > dis[u] + G[i].w){
                    dis[v] = dis[u] + G[i].w;
                    if(!inq[v])
                        Q.push(v),inq[v] = 1;
                }
            }
        }
        return dis[ed] < inf;
    }
    int dfn[maxn], low[maxn], place[maxn], scc, idx;
    stack <int> t;
    void tarjan(int u){
        dfn[u] = low[u] = ++idx;
        ins[u] = 1; t.push(u);
     
        for(int i = hh[u]; i; i = g[i].nxt){
            int v = g[i].v;
            if(!dfn[v]){
                tarjan(v);
                low[u] = min(low[u], low[v]);
            }
            else if(ins[v]) low[u] = min(low[u], dfn[v]);
        }
     
        if(low[u] == dfn[u]){
            scc++;
            while(1){
                int tt = t.top(); t.pop();
                place[tt] = scc; ins[tt] = 0;
                if(tt == u)break;
            }
        }
    }
     
    int main()
    {
        scanf("%d%d%d", &n, &m, &k);
        for(int i = 1; i <= m; i++){
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            add1(u, v, w);
        }
        for(int i = 1; i <= n; i++)
            if(!dfn[i])tarjan(i);
        for(int i = 1; i <= n; i++)
        for(int j = hh[i]; j; j = g[j].nxt){
            int v = g[j].v;
            if(place[v] != place[i])
                add(place[i], place[v], g[j].w);
        }
        for(int i = 1; i <= k; i++){
            scanf("%d%d", &st, &ed);
            st = place[st], ed = place[ed];
            if(spfa())printf("%d
    ", dis[ed]);
            else printf("No
    ");
        }
     
    }
    View Code

    今天本来是欢乐ak赛,但空间开小了,数据范围啊

  • 相关阅读:
    (笔记)Mysql命令mysqldump:备份数据库
    (笔记)Mysql命令rename:修改表名
    (笔记)Mysql命令alter add:增加表的字段
    (笔记)Mysql命令update set:修改表中的数据
    (笔记)Mysql命令delete from:删除记录
    (笔记)Mysql命令select from:查询表中的数据(记录)
    psutil库
    生成器 yield
    高阶函数map(),filter(),reduce()
    logging模块
  • 原文地址:https://www.cnblogs.com/EdSheeran/p/9275360.html
Copyright © 2020-2023  润新知