A. Lineland Mail (567A Lineland Mail)
题目描述:
给出n个升序排列的数,问每个数与除自身之外的n-1个数的绝对值最大和最小分别是什么?
解题思路:
因为给出的序列已经排好序了,对于每个数绝对值最小的肯定是左右相邻的两个数的绝对值取最小啦,绝对值最大就是和第一个数或者最后一个数的绝对值取最大咯,第一个数和最后一个数特殊处理一下就好啦。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <queue> 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 #include <algorithm> 7 using namespace std; 8 9 const int maxn = 100005; 10 int a[maxn]; 11 int main () 12 { 13 int n; 14 while (scanf ("%d", &n) != EOF) 15 { 16 for (int i=1; i<=n; i++) 17 scanf ("%d", &a[i]); 18 a[0] = a[2], a[n+1] = a[n-1]; 19 for (int i=1; i<=n; i++) 20 { 21 __int64 Min, Max; 22 Min = min (abs(a[i]-a[i-1]), abs(a[i]-a[i+1])); 23 Max = max (abs(a[i]-a[1]), abs(a[i]-a[n])); 24 printf ("%I64d %I64d ", Min, Max); 25 } 26 } 27 return 0; 28 }
B. Berland National Library (567B Berland National Library)
题目描述:
有一个图书馆管理系统,可以记录同学们的进去记录。每个同学都有一个图书编号,现在给一段记录,问根据这段记录,图书馆至少要能容纳多少的人?
解题思路:
因为每个童鞋都有一个不同的编号,当童鞋进图书馆的话,那么这个同学刚开始肯定不在图书馆,当童鞋出图书馆的话,这个同学有可能是在这段记录之前进入图书馆的,也有可能是在记录中进去图书馆的。注意到这一点后,模拟一下就ok啦。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <queue> 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 #include <algorithm> 7 using namespace std; 8 9 const int maxn = 1000005; 10 bool vis[maxn]; 11 int main () 12 { 13 int t, Max, num, ans; 14 while (scanf("%d", &t) != EOF) 15 { 16 Max = ans = 0; 17 memset (vis, false, sizeof(vis)); 18 while (t --) 19 { 20 char s[2]; 21 scanf ("%s %d", s, &num); 22 if (s[0] == '-') 23 { 24 if (!vis[num]) 25 Max ++; 26 else 27 ans --; 28 vis[num] = false; 29 } 30 else 31 { 32 ans ++; 33 vis[num] = true; 34 } 35 Max = max (Max, ans); 36 } 37 printf ("%d ", Max); 38 } 39 return 0; 40 }
C. Geometric Progression (567C Geometric Progression)
题目描述:
有n个数,从中找出三个数ai,aj,ak(其中i<j<k)满足等比为k的等比数列,问一共有多少组这个数满足这样的条件?
解题思路:
因为三个数满足a*k^(i-1), a*k^i, a*k*(i+1), 所以我们可以对每个数ai计算前面的ai/k个数x,以及后面的ai*k个数y,然后x*y累加起来。现在问题出来了,怎么又快又准确的找到x和y,因为ai范围很大,无法直接用ai当做下标用hash存储,所以我们要对ai离散化一下,然后每次二分查找找到ai的所在位置进行计算。也可以用map来实现离散化,虽然实现起来黑箱操作方便很多,但是效率并没有手动实现来的快,两个代码都贴一下。
手动离散化
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn = 200005; 4 typedef long long LL; 5 LL a[maxn], b[maxn], l[maxn], r[maxn], n, m; 6 LL bin_sreach (LL x) 7 { 8 LL high = m-1, low = 0; 9 while (high >= low) 10 { 11 LL mid = (high + low) / 2; 12 if (b[mid] == x) 13 return mid; 14 if (b[mid] < x) 15 low = mid + 1; 16 else 17 high = mid -1; 18 } 19 return n; 20 } 21 int main () 22 { 23 LL k; 24 while (scanf ("%I64d %I64d", &n, &k) != EOF) 25 { 26 for (LL i=0; i<n; i++) 27 { 28 scanf ("%I64d", &a[i]); 29 b[i] = a[i]; 30 } 31 sort (b, b+n); 32 m = unique(b, b+n) - b; 33 memset (l, 0, sizeof(l)); 34 memset (r, 0, sizeof(r)); 35 for (LL i=0; i<n; i++) 36 { 37 LL x = bin_sreach(a[i]); 38 l[x] ++; 39 } 40 __int64 ans = 0; 41 for (LL i=n-1; i>=0; i--) 42 { 43 LL x, y, z; 44 x = y = n; 45 z = bin_sreach(a[i]); 46 l[z] --; 47 if (a[i] % k == 0) 48 x = bin_sreach(a[i]/k); 49 y = bin_sreach(a[i]*k); 50 ans += l[x] * r[y]; 51 r[z] ++; 52 } 53 printf ("%I64d ", ans); 54 } 55 return 0; 56 }
map离散化
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //map离散化 2 #include <bits/stdc++.h> 3 using namespace std; 4 typedef __int64 LL; 5 const int maxn = 200005; 6 LL a[maxn]; 7 map <LL, LL> map1, map2; 8 int main () 9 { 10 LL k, n; 11 while (scanf ("%I64d %I64d", &n, &k) != EOF) 12 { 13 map1.clear(); 14 map2.clear(); 15 for (int i=1; i<=n; i++) 16 { 17 scanf ("%I64d", &a[i]); 18 map1[a[i]] ++; 19 } 20 LL ans = 0; 21 for (int i=n; i>0; i--) 22 { 23 map1[a[i]] --; 24 if (a[i]%k == 0) 25 ans += map1[a[i]/k] * map2[a[i]*k]; 26 map2[a[i]] ++; 27 } 28 printf ("%I64d ", ans); 29 } 30 return 0; 31 }
D. One-Dimensional Battle Ships(567C Geometric Progression)
题目描述:
有一个1*n的格子,Alice要放置k个1*a的军舰,每个军舰不能重叠不能相邻。Bob按照放置m个炸弹位置分别是a1,a2,a2....am。Alice是一个磨人的小妖精,她骗Bob说:Bob的m个炸弹都在没有放置军舰的格子,现在问Bob至少要引爆几个炸弹才能揭穿Alice的美丽谎言。
解题思路:
当没有放置炸弹的时候可以放置的军舰是最多的,每放置一个炸弹就有可能减少军舰的放置数目。可以先假设Alice是诚实善良的小公举,然后我们按照Bob放置炸弹的顺序引爆炸弹,比较引爆炸弹后最多放置的军舰ans与k,当ans<k的时候就说明Alice说谎。
如果按照Bob引爆炸弹的顺序模拟,每次保存可以放置炸弹的区间。最坏的情况下最后可能会有10^5个区间,复杂度大概为O(n*n/2),稳稳T,不商量。
所以我们可以用二分枚举答案,然后去判断当前枚举答案是否正确即可,这样优化后复杂度立马变为O(4*n*log2n)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn = 200005; 4 int shot1[maxn], shot2[maxn]; 5 int n, m, a, k; 6 int solve (int x) 7 { 8 for (int i=1; i<=x; i++) 9 shot2[i] = shot1[i]; 10 int ans = 0; 11 shot2[0] = 0; 12 shot2[x+1] = n+1; 13 sort (shot2, shot2+x+2); 14 for (int i=1; i<=x+1; i++) 15 ans += (shot2[i] - shot2[i-1]) / (a + 1); 16 return ans; 17 } 18 int main () 19 { 20 while (scanf ("%d %d %d", &n, &k, &a) != EOF) 21 { 22 scanf ("%d", &m); 23 for (int i=1; i<=m; i++) 24 scanf ("%d", &shot1[i]); 25 int low = 1, high = m, mid, res = n + 1; 26 while (low <= high) 27 { 28 mid = (low + high) / 2; 29 int ans = solve (mid); 30 31 if (ans >= k) 32 33 low = mid + 1; 34 else 35 { 36 high = mid - 1; 37 res = min (res, mid); 38 } 39 } 40 if (res == n+1) 41 res = -1; 42 printf ("%d ", res); 43 } 44 return 0; 45 }