• NOIP 2015 提高组 Day2


    期望得分:100+10+60=170

    实际得分:100+10+35=145

    http://www.cogs.pro/cogs/page/page.php?aid=16

    T1 跳石头

    时间限制:1 s   内存限制:256 MB

    【题目描述】

    这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选

    择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终 点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达 终点。

    为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳 跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能 移走起点和终点的岩石)。

    【输入格式】

    输入文件第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终 点之间的岩石数,以及组委会至多移走的岩石数。

    接下来 N 行,每行一个整数,第 i 行的整数 Di(0 < Di < L)表示第 i 块岩石与 起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同 一个位置。

    【输出格式】

    输出文件只包含一个整数,即最短跳跃距离的最大值。

    【样例输入】

    25 5 2 
    2
    11
    14
    17 
    21

    【样例输出】

    4

    【提示】

    输入输出样例 1 说明:将与起点距离为 2 和 14 的两个岩石移走后,最短的跳跃距离为 4(从与起点距离 17 的岩石跳到距离 21 的岩石,或者从距离 21 的岩石跳到终点)。

    另:对于 20%的数据,0 ≤ M ≤ N ≤ 10。 对于50%的数据,0 ≤ M ≤ N ≤ 100。

    对于 100%的数据,0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000。

     二分最短距离

    如果两块石头间距离比二分的还要短,

    若 此时还能移走石头,移走

    否则return false

    #include<cstdio>
    #define N 50011
    using namespace std;
    int L,n,m,ans;
    int a[N];
    bool check(int k)
    {
        int last=0,tot=0;
        for(int i=1;i<=n+1;i++) 
         if(a[i]-a[last]<k) 
         {
             if(tot<m) tot++; 
            else  return false;
         }
         else last=i;
         return true;
    }
    int main()
    {
        scanf("%d%d%d",&L,&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        int l=0,r=L,mid;
        a[n+1]=L;
        while(l<=r)
        {
            mid=l+r>>1;
            if(check(mid)) ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d",ans);
    }
    View Code

    T2 子串
    时间限制:1 s   内存限制:128 MB

    【题目描述】

    有两个仅包含小写英文字母的字符串 A 和 B。现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一 个新的字符串,请问有多少种方案可以使得这个新串与字符串 B 相等?注意:子串取出 的位置不同也认为是不同的方案。

    【输入格式】

    第一行是三个正整数 n,m,k,分别表示字符串 A 的长度,字符串 B 的长度,以及问题描述中所提到的 k,每两个整数之间用一个空格隔开。 第二行包含一个长度为 n 的字符串,表示字符串 A。 第三行包含一个长度为 m 的字符串,表示字符串 B。

    【输出格式】

    输出共一行,包含一个整数,表示所求方案数。由于答案可能很大,所以这里要求输出答案对 1,000,000,007 取模的结果。

    【样例输入1】

    6 3 1 
    aabaab 
    aab

    【样例输出1】

    2

    【样例输入2】

    6 3 2 
    aabaab 
    aab

    【样例输出2】

    7

    【样例输入3】

    6 3 3 
    aabaab 
    aab

    【样例输出1】

    7

    【提示】

    对于第 1 组数据:1≤n≤500,1≤m≤50,k=1;

    对于第 2 组至第 3 组数据:1≤n≤500,1≤m≤50,k=2;

    对于第 4 组至第 5 组数据:1≤n≤500,1≤m≤50,k=m;

    对于第 1 组至第 7 组数据:1≤n≤500,1≤m≤50,1≤k≤m;

    对于第 1 组至第 9 组数据:1≤n≤1000,1≤m≤100,1≤k≤m;

    对于所有 10 组数据:1≤n≤1000,1≤m≤200,1≤k≤m。

    令 f[i][j][k][0/1]表示A串前i个,分为k部分,匹配B串前j个,A串第i个不用/用 的方案数

    状态转移方程:

    如果i不用,那他就是前i-1个的和  f[i][j][k][0]=f[i-1][j][k][0]+f[i-1][j][k][1]

    如果i用,条件是a[i]=b[j] 那么它有2种决策:自成一堆,与前一个合为一堆

               自成一堆的话,他的前一个可能用了,也可能没用,又分为了两种

    所以 f[i][j][k][1]= f[i-1][j-1][k-1][1](自成一堆,前一个用了)

                           + f[i-1][j-1][k-1][0] (自成一堆,前一个没用)

                            + f[i-1][j-1][k][1] (与前一个合为一堆,前一个必须用了)

    边界条件:f[i][0][0][0]=1

    边界研究了一晚上

    f数组累积的前提是 a串中的第i个与b串的第一个相等

    这时第i个只能自成一堆,f[i-1][1-1][k-1][0] = 1

    最后用滚动数组滚动起来就A了

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,m,p,ans,s;
    char a[1001],b[201];
    int f[2][1001][201][2];
    const int mod=1000000007;
    int main()
    {
        scanf("%d%d%d",&n,&m,&p);
        scanf("%s%s",a+1,b+1);
        f[0][0][0][0]=f[1][0][0][0]=1;
        for(int i=1;i<=n;i++)
        {
            int t=i%2;
            for(int j=1;j<=min(i,m);j++)
              for(int k=1;k<=min(j,p);k++)
               {
                       f[t][j][k][0]=(f[t^1][j][k][0]+f[t^1][j][k][1])%mod;
                       if(a[i]==b[j])   f[t][j][k][1]=((f[t^1][j-1][k-1][0]+f[t^1][j-1][k-1][1])%mod+f[t^1][j-1][k][1])%mod;
                       else f[t][j][k][1]=0;
               }
            ans=(ans+f[t][m][p][1])%mod;
        }
    }
    View Code
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int n, m, k;
    long long f[2][205][205], s[2][205][205];
    char a[1005], b[205];
    const int mod = 1000000007;
    int main()
    {
        scanf("%d%d%d", &n, &m, &k);
        scanf("%s", a + 1);
        scanf("%s", b + 1);
        int now = 1, last = 0;
        f[0][0][0] = 1;
        for (int i = 1; i <= n; i++)
        {
            f[now][0][0] = 1;
            for (int j = 1; j <= min(i,m); j++)
                for (int kk = 1; kk <= min(j,k); kk++)
                {
                    if (a[i] == b[j])
                        s[now][j][kk] = (s[last][j - 1][kk] + f[last][j - 1][kk - 1]) % mod;
                    else
                        s[now][j][kk] = 0;
                    f[now][j][kk] = (f[last][j][kk] + s[now][j][kk]) % mod;
                }
            swap(now, last); 
        }
        printf("%lld
    ", f[last][m][k]);
        return 0;
    }
    View Code

    T3 运输计划

    时间限制:1 s   内存限制:256 MB

    【题目描述】

    公元 2044 年,人类进入了宇宙纪元。

    L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球。

    小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物

    流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰。

    为了鼓励科技创新,L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小 P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。

    在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后, 这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的 物流公司的阶段性工作就完成了。

    如果小 P 可以自由选择将哪一条航道改造成虫洞,试求出小 P 的物流公司完成阶段 性工作所需要的最短时间是多少?

    【输入格式】

    第一行包括两个正整数 n、m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。

    接下来 n-1 行描述航道的建设情况,其中第 i 行包含三个整数 ai, bi 和 ti,表示第i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。 接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j 个运输计划是从 uj 号星球飞往 vj 号星球。

    【输出格式】

    共 1 行,包含 1 个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。

    【样例输入】

    6 3 
    1 2 3 
    1 6 4 
    3 1 7 
    4 3 6 
    3 5 5 
    3 6 
    2 5 
    4 5

    【样例输出】

    11

    【提示】

    所有测试数据的范围和特点如下表所示

    请注意常数因子带来的程序效率上的影响。

    所需 技能:二分、lca、树链剖分、树上差分

    求最值,很容易想到二分,那么我们就二分这个值k

    如果最短时间为k,那么如果一个计划耗时超过k,那么它所经过的路径上就要有虫洞

    可能有不止一个计划耗时超过k,

    那么有可能改造为虫洞的路径是 所有超时的计划路径的交集

    如果这个交集中有一条路径,耗时>= 超出的时间(即不建虫洞的最大耗时-二分值)

    那么把他改为虫洞可以使时间减少

    现在就差如何求 计划耗时 和 交集

    计划耗时:树链剖分求lca,同时求出点到根节点的距离dis

                 计划耗时=dis[起点]+dis[终点]-2*dis[lca]

    交集:树上差分

            计划起点+1,终点+1,lca-2

            然后从叶子节点向上累加

           若点的sum等于超时计划数,则点与父节点之间的边在交集中

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #define N 300001
    using namespace std;
    int n,m,l,r,mid,ans,maxn;
    int front[N],nextt[N*2],to[N*2],tot,w[N*2];
    int son[N],fa[N],dis[N],deep[N];
    int id[N],bl[N],cnt;
    int sum[N];
    struct node
    {
        int s,t,lca,dis;
    }e[N];
    void add(int u,int v,int x)
    {
        to[++tot]=v; nextt[tot]=front[u]; front[u]=tot; w[tot]=x;
    }
    void dfs(int x)
    {
        son[x]++;
        for(int i=front[x];i;i=nextt[i])
        {
            if(to[i]==fa[x]) continue;
            fa[to[i]]=x;
            dis[to[i]]=dis[x]+w[i];
            deep[to[i]]=deep[x]+1;
            dfs(to[i]);
            son[x]+=son[to[i]];
        }
    }
    void dfs2(int x,int top)
    {
        id[x]=++cnt;
        bl[x]=top;
        int y=0;
        for(int i=front[x];i;i=nextt[i])
        {
            if(to[i]==fa[x]) continue;
            if(son[to[i]]>son[y]) y=to[i];
        }
        if(!y) return;
        dfs2(y,top);
        for(int i=front[x];i;i=nextt[i])
        {
            if(to[i]==fa[x]||to[i]==y) continue;
            dfs2(to[i],to[i]);
        }
    }
    void dfs3(int x)
    {
        for(int i=front[x];i;i=nextt[i])
        {
            if(to[i]==fa[x]) continue;
            dfs3(to[i]);
            sum[x]+=sum[to[i]];
        }
    }
    int get_lca(int x,int y)
    {
        while(bl[x]!=bl[y])
        {
            if(deep[bl[x]]<deep[bl[y]]) swap(x,y);
            x=fa[bl[x]];
        }
        if(deep[x]>deep[y]) x=y;
        return x; 
    }
    bool check(int k)
    {
        memset(sum,0,sizeof(sum));
        int h=0;
        for(int i=1;i<=m;i++)
        {
            if(e[i].dis<=k) continue;
            sum[e[i].s]++; sum[e[i].t]++; sum[e[i].lca]-=2;
            h++;
        }
        dfs3(1);
        for(int i=1;i<=n;i++) if(sum[i]==h&&dis[i]-dis[fa[i]]>=maxn-k) return true;
        return false; 
    }
    int main()
    {
        /*freopen("transport.in","r",stdin);
        freopen("transport.out","w",stdout);*/
        scanf("%d%d",&n,&m);
        int u,v,a;
        for(int i=1;i<n;i++) 
        {
            scanf("%d%d%d",&u,&v,&a);
            add(u,v,a);
            add(v,u,a);
        }
        for(int i=1;i<=m;i++) scanf("%d%d",&e[i].s,&e[i].t);
        dfs(1);
        dfs2(1,0);
        for(int i=1;i<=m;i++) 
        {
            e[i].lca=get_lca(e[i].s,e[i].t);
            e[i].dis=dis[e[i].s]+dis[e[i].t]-2*dis[e[i].lca];
            maxn=max(maxn,e[i].dis);
        }
        r=maxn;
        while(l<=r)
        {
            mid=l+r>>1;
            if(check(mid)) { ans=mid; r=mid-1;}
            else l=mid+1;
        }
        printf("%d",ans);
    }
    View Code
  • 相关阅读:
    微信开发者工具http申请图片变成https
    vue 中v-for img src 路径加载问题
    nodejs内置模块querystring中parse使用问题
    用git上传项目到github遇到的问题和解决方法
    页面刷新——微信小程序生命周期探索
    小程序项目复盘(三) 用全局变量传参的问题
    小程序项目复盘(二) wx.request异步请求处理
    小程序项目复盘(一)字符串处理问题
    微信小程序中我常用到的CSS3弹性盒子布局(flex)总结
    wx.request中POST方法传参问题,用到JSON.stringify()
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6750111.html
Copyright © 2020-2023  润新知