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('\n');
136     }
137     return 0;
138 }
View Code

 

posted @ 2017-07-04 15:23  嘒彼小星  阅读(281)  评论(0编辑  收藏  举报