NOIP模拟 7.03
Problem 1 抓牛(catchcow.cpp/c/pas)
【题目描述】
农夫约翰被通知,他的一只奶牛逃逸了!所以他决定,马上出发,尽快把那只奶牛抓回来.
他们都站在数轴上.约翰在N(O≤N≤100000)处,奶牛在K(O≤K≤100000)处.约翰有两种办法移动,步行和瞬移:步行每秒种可以让约翰从x处走到x+l或x-l处;而瞬移则可让他在1秒内从x处消失,在2x处出现.然而那只逃逸的奶牛,悲剧地没有发现自己的处境多么糟糕,正站在那儿一动不动.
那么,约翰需要多少时间抓住那只牛呢?
【输入格式】
仅有两个整数N和K
【输出格式】
最短时间
【样例输入】
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 }
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 }
Problem 3 教主的魔法(magic.cpp/c/pas)
【题目描述】
教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1、2、……、N。
每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)
CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。M”,则紧接着有三个数字L
WD巨懒,于是他把这个回答的任务交给了你。
【输入格式】
第1行为两个整数N、Q。Q为问题数与教主的施法数总和。
第2行有N个正整数,第i个数代表第i个英雄的身高。
第3到第Q+2行每行有一个操作:
(1)若第一个字母为“、R、W。表示对闭区间 [L, R] 内所有英雄的身高加上W。
(2)若第一个字母为“A”,则紧接着有三个数字L、R、C。询问闭区间 [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个英雄身高为1、2、3、4、5,此时[1, 5]间有2个英雄的身高大于等于4。教主施法后变为1、2、4、5、6,此时[1, 5]间有3个英雄的身高大于等于4。
【数据范围】
对30%的数据,N≤1000,Q≤1000。
对100%的数据,N≤1000000,Q≤3000,1≤W≤1000,1≤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 }