• NOIP模拟 7.03


    Problem 1 抓牛(catchcow.cpp/c/pas)

    【题目描述】

           农夫约翰被通知,他的一只奶牛逃逸了!所以他决定,马上发,尽快把那只奶牛抓回来.

    他们都站在数轴上.约翰在N(O≤N≤100000)处,奶牛在K(O≤K≤100000)处.约翰有两种办法移动,步行和瞬移:步行每秒种可以让约翰从x处走到x+lx-l处;而瞬移则可让他在1秒内从x处消失,在2x处出现.然而那只逃逸的奶牛,悲剧地没有发现自己的处境多么糟糕,正站在那儿一动不动.

           那么,约翰需要多少时间抓住那只牛呢?

    【输入格式】

    仅有两个整数NK

    【输出格式】

    最短时间

    【样例输入】

    5 17

    【样例输出】

    4

    【题解】

    广搜水过

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cstdlib>
     5 #include <algorithm>
     6 
     7 inline void read(int &x){x = 0;char ch = getchar();char c = ch;while(ch > '9' || ch < '0')c = ch, ch = getchar();while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar();if(c == '-')x = -x;}
     8 const int INF = 0x3f3f3f3f;
     9 const int MAXN = 400000;
    10 
    11 int n,k;
    12 int b[MAXN][3];
    13 //状态标号:0:x+1  1:x - 1  2:x * 2 
    14 struct Node
    15 {
    16     int x,flag,step;
    17 }queue[2500000];
    18 int head,tail;
    19 int ans;
    20 
    21 inline void bfs()
    22 {
    23     //[head, tail]
    24     if(n >= k)
    25     {
    26         ans = n - k;return;
    27     }
    28     if((n + 1 == k || n - 1 == k) || ((n << 1) == k))
    29     {
    30         ans = 1;return;
    31     }
    32     head = 1, tail = 0;
    33     queue[++tail] = Node{n - 1, 0, 1};
    34     queue[++tail] = Node{n + 1, 1, 1};
    35     queue[++tail] = Node{(n << 1), 2, 1};
    36     b[n - 1][0] = true;
    37     b[n + 1][1] = true;
    38     b[(n << 1)][2] = true;
    39     register Node tmp;
    40     while(head <= tail)
    41     {
    42         tmp = queue[head ++];
    43         for(int i = 0;i < 3;++ i)
    44         {
    45             if(i == 0 && tmp.x - 1 >= 0 && !b[tmp.x - 1][0])
    46             {
    47                 if(tmp.x - 1 == k)
    48                 {
    49                     ans = tmp.step + 1;
    50                     return;
    51                 }
    52                 queue[++tail] = Node{tmp.x - 1, 0, tmp.step + 1};
    53                 b[tmp.x - 1][0] = true;        
    54             }
    55             else if(i == 1 && tmp.x + 1 <= 200000 && !b[tmp.x + 1][1])
    56             {
    57                 if(tmp.x + 1 == k)
    58                 {
    59                     ans = tmp.step + 1;
    60                     return;
    61                 }
    62                 queue[++tail] = Node{tmp.x + 1, 1, tmp.step + 1};
    63                 b[tmp.x + 1][1] = true;
    64             }
    65             else if(tmp.x * 2 <= 200000 && !b[(tmp.x << 1)][2])
    66             {
    67                 if((tmp.x << 1) == k)
    68                 {
    69                     ans = tmp.step + 1;
    70                     return;
    71                 }
    72                 queue[++tail] = Node{(tmp.x << 1), 2, tmp.step + 1};
    73                 b[(tmp.x << 1)][2] = true;
    74             }
    75         }
    76     }
    77 }
    78 
    79 int main()
    80 {
    81     read(n);read(k);
    82     bfs(); 
    83     printf("%d", ans);
    84     return 0;
    85 }
    View Code

    Problem 2 路面修整(grading.cpp/c/pas)

    【题目描述】

    FJ打算好好修一下农场中某条凹凸不平的土路。按奶牛们的要求,修好后的路面高度应当单调上升或单调下降,也就是说,高度上升与高度下降的路段不能同时出现在修好的路中。 整条路被分成了N段,N个整数A_1, ... , A_N (1 <= N <= 2,000)依次描述了每一段路的高度(0 <= A_i <= 1,000,000,000)FJ希望找到一个恰好含N个元素的不上升或不下降序列B_1, ... , B_N,作为修过的路中每个路段的高度。由于将每一段路垫高或挖低一个单位的花费相同,修路的总支出可以表示为: |A_1 - B_1| + |A_2 - B_2| + ... + |A_N - B_N| 请你计算一下,FJ在这项工程上的最小支出是多少。FJ向你保证,这个支出不会超过2^31-1【输入格式】
     1: 输入1个整数:N * 2..N+1: i+1行为1个整数:A_i

    【输出格式】
    1: 输出1个正整数,表示FJ把路修成高度不上升或高度不下降的最小花费

    【样例输入】

    7
    1
    3
    2
    4
    5
    3
    9

    【样例输出】

    3

    【样例解释】

    FJ将第一个高度为3的路段的高度减少为2,将第二个高度为3的路段的高度增加到5,总花费为|2-3|+|5-3| = 3,并且各路段的高度为一个不下降序列 1,2,2,4,5,5,9

     

    【题解】

    DP题。

    【状态定义】

    dp[i][j]表示前i个数,最后一个数在原数组中是第j大/小的最小价值。记录cnt[i]为第i大/小得数,num[i]为第i个位置的数

    【转移】
    dp[i][j] = min{dp[i - 1][k] + abs(num[i] - cnt[j])} 1 <= k <= j

    【优化】
    1、状态更新是O(n)的,我们可以用mi[i][j]表示dp[i][1..j]的最小值
    2、这里还有一个技巧,我们可以让dp[i][j]表示前i个数,最后一个数在原数组中是前j大 的最小价值
       转移有
       dp[i][j] = min{dp[i][j - 1], dp[i - 1][j] + abs(num[i] - cnt[j])} 
       同时也可以使用滚动数组进行优化

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cstdlib>
     5 #include <algorithm>
     6 inline int min(int a, int b){return a > b ? b : a;}
     7 inline void read(int &x){x = 0;char ch = getchar();char c = ch;while(ch > '9' || ch < '0')c = ch, ch = getchar();while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar();if(c == '-')x = -x;}
     8 const int INF = 0x3f3f3f3f;
     9 const int MAXN = 2000 + 10;
    10 
    11 int dp[2][MAXN], num[MAXN], cnt[MAXN], recnt[MAXN],ans,n;
    12 
    13 int main()
    14 {
    15     read(n);
    16     for(register int i = 1;i <= n;++ i)
    17     {
    18         read(num[i]);
    19         recnt[i] = cnt[i] = num[i];
    20     }
    21     std::sort(cnt + 1, cnt + 1 + n);
    22     std::sort(recnt + 1, recnt + 1 + n, std::greater<int>());
    23     register int tmp = 1;
    24     for(register int i = 1;i <= n;++ i)
    25     {
    26         for(register int j = 1;j <= n;++ j)
    27         {
    28             if(j == 1)
    29                 dp[tmp][j] = dp[tmp ^ 1][j] + abs(cnt[j] - num[i]);
    30             else
    31                 dp[tmp][j] = min(dp[tmp][j - 1], dp[tmp ^ 1][j] + abs(cnt[j] - num[i]));
    32         }
    33         tmp ^= 1;
    34     }
    35     ans = dp[n & 1][n];
    36     tmp = 1;
    37     for(register int i = 1;i <= n;++ i)
    38     {
    39         for(register int j = 1;j <= n;++ j)
    40         {
    41             if(j == 1)
    42                 dp[tmp][j] = dp[tmp ^ 1][j] + abs(recnt[j] - num[i]);
    43             else
    44                 dp[tmp][j] = min(dp[tmp][j - 1], dp[tmp ^ 1][j] + abs(recnt[j] - num[i]));
    45         }
    46         tmp ^= 1;
    47     }
    48     ans = min(ans, dp[n & 1][n]);
    49     printf("%d", ans);
    50     return 0;
    51 }
    View Code

    Problem 3 教主的魔法(magic.cpp/c/pas)

    【题目描述】

    教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为12……N

    每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R]1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第LR)个英雄的身高)

    CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。M”,则紧接着有三个数字L

    WD巨懒,于是他把这个回答的任务交给了你。

    【输入格式】

     1行为两个整数NQQ为问题数与教主的施法数总和。

     2行有N个正整数,第i个数代表第i个英雄的身高。

     3到第Q+2行每行有一个操作:

    1)若第一个字母为RW。表示对闭区间 [L, R] 内所有英雄的身高加上W

    2若第一个字母为“A”,则紧接着有三个数字LRC。询问闭区间 [L, R] 内有多少英雄的身高大于等于C

    【输出格式】

         对每个“A”询问输出一行,仅含一个整数,表示闭区间 [L, R] 内身高大于等于C的英雄数。

     【样例输入】

    5 3

    1 2 3 4 5

    A 1 5 4

    M 3 5 1

    A 1 5 4

    【样例输出】

    2

    3

    【数据范围】

    【输入输出样例说明】

    原先5个英雄身高为12345,此时[1, 5]间有2个英雄的身高大于等于4。教主施法后变为12456,此时[1, 5]间有3个英雄的身高大于等于4

    【数据范围】

    30%的数据N≤1000Q≤1000

    100%的数据N≤1000000Q≤30001≤W≤10001≤C≤1,000,000,000

    【题解】

    分块+二分模板题,第一次写分块。。常数优化到天际

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <cstdlib>
      5 #include <algorithm>
      6 #include <cmath>
      7 
      8 inline void read(int &x){x = 0;char ch = getchar();char c = ch;while(ch > '9' || ch < '0')c = ch, ch = getchar();while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar();if(c == '-')x = -x;}
      9 inline int min(int a, int b){return a < b ? a : b;}
     10 void put(int x){if (x < 0)x = ~x + 1, putchar('-');if (x > 9) put(x / 10);putchar(x % 10 + 48);}
     11 const int INF = 0x3f3f3f3f;
     12 const int MAXN = 1000000 + 10;
     13 
     14 int n,q;
     15 int squ[MAXN],num[MAXN],pos[MAXN],block,flag[MAXN],size;
     16 int L[MAXN],R[MAXN];
     17 
     18 //对第x个块进行重新排序 
     19 int resort(int x)
     20 {
     21     int l = L[x];
     22     int r = R[x];
     23     for(register int i = l;i <= r;++ i)
     24         squ[i] = num[i];    
     25     std::sort(squ + l, squ + r + 1);
     26 }
     27 
     28 //对第x个块快内进行二分查找,返回找到的长度 
     29 int find(int x, int k)
     30 {
     31     int l = L[x];
     32     int r = R[x];
     33     int last = r;
     34     int mid;
     35     while(l <= r)
     36     {
     37         mid = (l + r) >> 1;
     38         if(squ[mid] < k)l = mid + 1;
     39         else r = mid - 1; 
     40     } 
     41     return last - l + 1;
     42 }
     43 
     44 //区间修改,[ll,rr]增加k 
     45 inline void modify(int ll, int rr, int k)
     46 {
     47     //同一个块则暴力修改
     48     if(pos[ll] == pos[rr]) 
     49     {
     50         for(register int i = ll;i <= rr;++ i)
     51             num[i] = num[i] + k;
     52         resort(pos[ll]);
     53         return;
     54     }
     55 
     56     //不同的块先改左右两边
     57     register int l = pos[ll],r = pos[rr];
     58     if(L[l] < ll)
     59     {
     60         for(register int i = ll;i <= R[l];++ i)
     61                num[i] = num[i] + k;
     62            resort(l);
     63            ++ l;
     64     }
     65     if(R[r] > rr)
     66     {
     67         for(register int i = L[r];i <= rr;++ i)
     68             num[i] = num[i] + k;
     69         resort(r);
     70         -- r;
     71     }
     72     for(register int i = l;i <= r;++ i)
     73         flag[i] = flag[i] + k;
     74 }
     75 
     76 //区间查询,[ll,rr]查询k
     77 
     78 int ask(int ll, int rr, int k)
     79 {
     80     register int ans = 0;
     81     if(pos[ll] == pos[rr])
     82     {
     83         for(int i = ll;i <= rr;++ i)
     84             if(num[i] + flag[pos[i]] >= k)ans ++;
     85         return ans;
     86     }
     87     register int l = pos[ll],r = pos[rr];
     88     if(L[l] < ll)
     89     {
     90         for(register int i = ll;i <= R[l];++ i)
     91             if(num[i] + flag[pos[i]]>= k)ans ++;
     92         ++ l;
     93     }
     94     if(R[r] > rr)
     95     {
     96         for(register int i = L[r];i <= rr;++ i)
     97                if(num[i] + flag[pos[i]]>= k)ans ++;
     98            -- r;
     99     }
    100     for(register int i = l;i <= r;++ i)
    101         ans = ans + find(i, k - flag[i]);
    102     return ans;
    103 } 
    104 
    105 int main()
    106 {
    107     read(n);read(q);
    108     
    109     register char c;
    110     register int tmp1,tmp2,tmp3;
    111     
    112     block = sqrt(n);
    113     if(n % block)
    114         size = n / block + 1;
    115     else 
    116         size = n / block;
    117         
    118     for(register int i = 1;i <= n;++ i)
    119     {
    120         read(num[i]);
    121         pos[i] = (i - 1) / block + 1;
    122         squ[i] = num[i];
    123     }
    124     for(register int i = 1;i <= size;++ i)
    125         L[i] = (i - 1) * block + 1,R[i] = i * block;
    126     if(R[size] > n)R[size] = n;
    127     for(register int i = 1;i <= size;++ i)
    128         resort(i);
    129         
    130     for(register int i = 1;i <= q;++ i)
    131     {
    132         c = getchar();while(c != 'M' && c != 'A')c = getchar();
    133         read(tmp1);read(tmp2);read(tmp3);
    134         if(c == 'M')modify(tmp1, tmp2, tmp3);
    135         else put(ask(tmp1, tmp2, tmp3)), putchar('
    ');
    136     }
    137     return 0;
    138 }
    View Code
  • 相关阅读:
    distroless 镜像介绍及调试基于distroless 镜像的容器
    C# 设置或验证 PDF中的文本域格式 E
    Java 在PDF中添加工具提示|ToolTip E
    MongoDB Security
    Spring Boot MongoDB
    MongoDB 安装
    nginx重试机制proxy_next_upstream
    (转)VC中等比例缩放图像
    5 Ways You can Learn Programming Faster
    如何批量去除文件名中的某些字符串?
  • 原文地址:https://www.cnblogs.com/huibixiaoxing/p/7111267.html
Copyright © 2020-2023  润新知