题意: 给你一个长度为7的序列,检查序列里面是否有1、2、3、5、6这五个数字,如果有输出YES,没有输出NO。
数据范围 $$1 \leq a_i \leq 7$$
题解:模拟 代码: 1 2 3 4 5 6 7 8 9 10 11 void JiuCherish () { for (int i=0 ;i<7 ;i++) std::cin >> a[i]; std::set<int > f = {1 ,2 ,3 ,5 ,6 }; for (int i=0 ;i<7 ;i++) { if (f.find (a[i]) == f.end ()) { std::cout << "NO" << endl; return ; } } std::cout << "YES" << endl; }
题意: B有n个碗,第i个碗容量为ai。
B要求:A的碗至少要比B家一半数量的碗容量更小,为了装更多的饭,A想带尽可能大的碗,问最大能带多大的碗。
数据范围 $$1\leq n\leq 5 \times 10^5$$
$$1 \leq a_i \leq 10^6$$
题解:排序 代码: 1 2 3 4 5 6 7 8 void JiuCherish () { int n; std::cin >> n; std::vector<int > f (n) ; for (int i=0 ;i<n;i++) std::cin >> f[i]; std::sort (f.begin (),f.end ()); std::cout << f[n / 2 ] - 1 << endl; }
题意: 好矩阵的定义需要满足以下两个条件:
1.矩阵的行数和列数都是偶数。
2.将左上角的区域全部变为1,右上右下左下的区域变成0。
现在给你一个n行n列的01矩阵,你每次操作只能交换两个相邻字符,请你将矩阵变成好矩阵,保证给出的矩阵一定有解,需要给出的操作次数不超过n³/2,可以证明,在该范围内至少存在一种合法解。
输出操作次数,以及每一次的交换方案。
数据范围 $$1\leq T\leq 20$$
$$2 \leq n \leq 100$$
$$保证1的数量恰好为\frac{n^2}{4}$$
题解:模拟 确定左上方有没有1,找最近的1让他移动过去。
代码:(学习konbi的) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 void JiuCherish () { int n; std::cin >> n; std::vector<std::vector<int >> f (n + 5 ,std::vector<int >(n + 5 )); for (int i=1 ;i<=n;i++) { std::string s; std::cin >> s; for (int j=1 ;j<=n;j++) { f[i][j] = s[j - 1 ] - '0' ; } } int m = n / 2 ; for (int i=1 ;i<=101 ;i++) { for (int j=1 ;j<=101 ;j++) { st[i][j] = false ; } } std::vector<std::array<int ,4>> ans; for (int i=1 ;i<=m;i++) { for (int j=1 ;j<=m;j++) { st[i][j] = true ; if (f[i][j]) continue ; int ok = 0 ; for (int x=1 ;!ok and x<=n;x++) { for (int y=1 ;!ok and y<=n;y++) { if (!f[x][y] or st[x][y]) continue ; ok = 1 ; while (y < j) { std::swap (f[x][y],f[x][y + 1 ]); ans.pb ({x,y,x,y + 1 }); y += 1 ; } while (x < i) { std::swap (f[x][y],f[x + 1 ][y]); ans.pb ({x,y,x + 1 ,y}); x += 1 ; } while (y > j) { std::swap (f[x][y],f[x][y - 1 ]); ans.pb ({x,y,x,y - 1 }); y -= 1 ; } while (x > i) { std::swap (f[x][y],f[x - 1 ][y]); ans.pb ({x,y,x - 1 ,y}); x -= 1 ; } } } } } std::cout << ans.size () << endl; for (auto [a,b,c,d] : ans) { std::cout << a << ' ' << b << ' ' << c << ' ' << d << endl; } }
题意: 我们定义双生数组:数组大小为偶数,且只有2种数组元素,并且这两种元素的出现次数相同。
现在给你一个数组,判断他是不是双生数组。
数据范围 $$1\leq T \leq 10^4$$
$$1 \leq n \leq 10^5$$
$$1 \leq a_i \leq 10^{9}$$
题解:模拟,stl 题意。
代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void JiuCherish () { int n; std::cin >> n; for (int i=1 ;i<=n;i++) std::cin >> a[i]; if (n % 2 != 0 ) { std::cout << "No" << endl; return ; } std::map<int ,int > mp; for (int i=1 ;i<=n;i++) mp[a[i]] += 1 ; if (mp.size () != 2 ) { std::cout << "No" << endl; return ; } else { auto it = mp.begin (); int fc = it->se; ++it; int sc = it->se; if (fc == sc) { std::cout << "Yes" << endl; } else { std::cout << "No" << endl; } } }
题意: 我们定义双生数组:数组大小为偶数,且只有2种数组元素,并且这两种元素的出现次数相同。
现在给你一个长度为偶数的数组,你可以进行若干次操作,每次操作选择一个元素,使其加1或者减1,小红希望这个数组变成双生数组的最小操作次数。
数据范围 $$1\leq T \leq 10^4$$
$$2 \leq n \leq 10^5$$
$$1 \leq a_i \leq 10^{9}$$
题解:模拟/二分/三分 将数组排序后分成两段,前n/2小的数变成x,后n/2大的数变成y,x和y通过二分/三分的形式找出来,x和y相同的情况下直接计算操作次数即可,否则分别计算(x+1,y)、(x-1,y)、(x,y+1)、(x,y-1)四种情况操作次数的最小值。
代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 void JiuCherish () { int n; std::cin >> n; for (int i=1 ;i<=n;i++) std::cin >> a[i]; std::sort (a + 1 ,a + n + 1 ); for (int i=1 ;i<=n/2 ;i++) { b[i] = a[i + n / 2 ]; } int m = n / 2 ; int l = 0 ,r = 8e18 ; int f1 = 0 ; int f2 = 0 ; auto check = [&](int mid,int a[]) { int res = 0 ; for (int i=1 ;i<=m;i++) { res += std::abs (mid - a[i]); } return res; }; while (l < r) { int mid = l + (r - l) / 2 ; int x1 = check (mid,a); int x2 = check (mid + 1 ,a); if (x1 <= x2) { r = mid; f1 = mid; } else { l = mid + 1 ; f1 = mid + 1 ; } } l = 0 ,r = 8e18 ; while (l < r) { int mid = l + (r - l) / 2 ; int x1 = check (mid,b); int x2 = check (mid + 1 ,b); if (x1 <= x2) { r = mid; f2 = mid; } else { l = mid + 1 ; f2 = mid + 1 ; } } if (f1 == f2) { int x1 = check (f1,a); int x2 = check (f2 - 1 ,b); int ans = x1 + x2; x1 = check (f1,a); x2 = check (f2 + 1 ,b); ans = std::min (ans,x1 + x2); x1 = check (f1 - 1 ,a); x2 = check (f2,b); ans = std::min (ans,x1 + x2); x1 = check (f1 + 1 ,a); x2 = check (f2,b); ans = std::min (ans,x1 + x2); std::cout << ans << endl; } else { int x1 = check (f1,a); int x2 = check (f2,b); std::cout << x1 + x2 << endl; } }
题意: 给定区间**[l,r]**,从中选取两个整数x和y,使得下式成立:
$$x + y = (x or y) + (x and y) + (x xor y)$$
有多少对不同的x,y满足条件。
其中or表示按位或运算,and表示按位与运算,xor表示按位异或运算。
数据范围 $$1 \leq T \leq 2\times10^5$$
$$0 \leq l \leq r \leq 10^{18}$$
题解:打表、位运算、结论 打表任意一个区间可以知道本质就是问[l,r]区间上有多少个数,
代码: 1 2 3 4 5 void JiuCherish () { int l,r; std::cin >> l >> r; std::cout << r - l + 1 << endl; }
题意: 定义:铸剑的温度越接近 n 度,剑的品质越好。
现在启动炉子时,A已经添过一次柴了,所以铸剑炉的初始温度为 m 度,此后每次添柴可以使得铸剑炉的温度提高到原来的 m 倍。
A想知道,最少需要添多少次柴,才能使得铸剑炉的温度最接近 n 度。
数据范围 $$1 \leq T \leq 10^5$$
$$1 \leq n,m \leq 10^{9}$$
题解:模拟 模拟每次添加的温度,注意需要计算最接近n的操作(是从小于n靠近还是从大于n靠近)。
特判m为1的时候只有最初第1次添柴。
代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void JiuCherish () { int n,m; std::cin >> n >> m; if (m == 1 ) { std::cout << 1 << endl; return ; } int ans = 1 ; int f1 = m; int add = std::abs (m - n); while (666 ) { if (add <= std::abs (n - m * f1)) { break ; } ans += 1 ; m *= f1; add = std::abs (m - n); } std::cout << ans << endl; }
题意: 划定一个矩形区域,左侧边界为x=a,右侧边界为x=b,下侧边界为y=c,上侧边界为y=d,希望在四条直线所围成的矩形的边界上找到三个不同的整数点A,B,C,使得过这三个点画出的圆半径最大。
原题弱化版:整整齐齐
数据范围 $$1 \leq n \leq 10^5$$
$$1 \leq l_i \leq r_i \leq n$$
题解:贪心 我们先将所有区间排序,用一个优先队列来维护“可选的区间右端点”。当我们指定某个区间为 i 的时候,我们首先将所有以 i 为左端点的区间加入优先队列,然后尽量选择目前最小的那个右端点即可。
代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void JiuCherish () { int n; std::cin >> n; std::vector<std::vector<int >> a; for (int i=0 ;i<n;i++) { int x,y; std::cin >> x >> y; x -= 1 ; y -= 1 ; a.pb ({x,y,i}); } std::sort (a.begin (),a.end ()); std::priority_queue<std::pair<int ,int >,std::vector<std::pair<int ,int >>,std::greater<std::pair<int ,int >>> pq; std::vector<int > ans (n) ; int p = 0 ; for (int i=0 ;i<n;i++) { while (p < n and a[p][0 ] <= i) { pq.push ({a[p][1 ],a[p][2 ]}); p += 1 ; } if (pq.size () == 0 ) { std::cout << -1 << endl; return ; } auto tp = pq.top (); pq.pop (); if (tp.fi < i) { std::cout << -1 << endl; return ; } ans[tp.se] = i; } for (auto x : ans) std::cout << x + 1 << ' ' ; }
题意: 数据范围 $$1 \leq n \leq 10^5$$
$$2000 \leq h \leq 2030$$
$$1 \leq m \leq 12$$
$$1 \leq user_{id} \leq 10^{20}$$
YYYY-MM-DD表示登录日期。
hh:mm:ss表示登录时间。
题解:模拟,set,字符串。 按题意模拟就好,同个人同个时间段登录用set去重即可。
代码:(我比较懒) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 def check (time, start, end ): return start <= time <= end def JiuCherish (): n, h, m = map (int , input ().split()) mc = set () ec = set () lb = set () bd = set () for i in range (n): ui, ld, lt = input ().split() ye, mo, day = ld.split('-' ) if int (ye) == h and int (mo) == m: if check(lt, "07:00:00" , "09:00:00" ): mc.add(ui) elif check(lt, "18:00:00" , "20:00:00" ): mc.add(ui) elif check(lt, "11:00:00" , "13:00:00" ): lb.add(ui) elif check(lt, "22:00:00" , "23:59:59" ) or check(lt, "00:00:00" , "01:00:00" ): bd.add(ui) print (len (mc), len (lb), len (bd)) if __name__ == "__main__" : JiuCherish()
题意: 有一个APP,其中的数据由三个字段构成,分别是user_id表示用户id,login_date表示登陆日期,login_time表示登陆时间。
现在需要帮忙统计h年m月份通勤、午休、临睡这三个时段各有多少人登陆过APP。特别地,同一个人在同一个时间段多次登录只认定为1次。
约定通勤时间为:7点到9点,18点到20点。
午休时间段为:11点到13点。
临睡时间段为:22点到第二天1点。
时间段均包含左右边界值。
数据范围 $$1 \leq n \leq 10^5$$
$$2000 \leq h \leq 2030$$
$$1 \leq m \leq 12$$
$$1 \leq user_{id} \leq 10^{20}$$
YYYY-MM-DD表示登录日期。
hh:mm:ss表示登录时间。
题解:模拟,set,字符串。 按题意模拟就好,同个人同个时间段登录用set去重即可。
代码:(我比较懒) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 def check (time, start, end ): return start <= time <= end def JiuCherish (): n, h, m = map (int , input ().split()) mc = set () ec = set () lb = set () bd = set () for i in range (n): ui, ld, lt = input ().split() ye, mo, day = ld.split('-' ) if int (ye) == h and int (mo) == m: if check(lt, "07:00:00" , "09:00:00" ): mc.add(ui) elif check(lt, "18:00:00" , "20:00:00" ): mc.add(ui) elif check(lt, "11:00:00" , "13:00:00" ): lb.add(ui) elif check(lt, "22:00:00" , "23:59:59" ) or check(lt, "00:00:00" , "01:00:00" ): bd.add(ui) print (len (mc), len (lb), len (bd)) if __name__ == "__main__" : JiuCherish()
题意: 给你一个n行m列的数表,需要记录每一个边界0的个数取最小值。
数据范围 $$1 \leq n,m \leq 500$$
$$s_{i,j}不是0就是1$$
题解:DFS,BFS,搜索 记录每一个边界0的个数取最小值就是答案。
代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 int dx[4 ] = {-1 ,0 ,1 ,0 };int dy[4 ] = {0 ,1 ,0 ,-1 };int f[550 ][550 ];bool vis[550 ][550 ];std::set<std::pair<int ,int >> s; int n,m;void dfs (int x,int y) { for (int i=0 ;i<4 ;i++) { int xx = x + dx[i],yy = y + dy[i]; if (xx < 1 or xx > n or yy < 1 or yy > m or vis[xx][yy]) { continue ; } if (f[xx][yy] == 0 ) { s.insert ({xx,yy}); continue ; } vis[xx][yy] = 1 ; dfs (xx,yy); } } void JiuCherish () { std::cin >> n >> m; for (int i=1 ;i<=n;i++) { std::string x; std::cin >> x; x = ' ' + x; for (int j=1 ;j<=m;j++) { f[i][j] = x[j] - '0' ; } } int ans = n * m; for (int i=1 ;i<=n;i++) { for (int j=1 ;j<=m;j++) { if (f[i][j] == 1 and !vis[i][j]) { s.clear (); dfs (i,j); ans = std::min (ans,(int )s.size ()); } } } std::cout << ans << endl; }
题意: 数据范围 $$1\leq T\leq 500$$
$$1 \leq l \leq 500,1 \leq x,y \leq \frac{l}{3}$$
$$保证字符串仅由0,1构成$$
题解:数学,裴蜀定理,gcd 首先考虑 x = y 的情况,很明显是做一个递推的过程,找到每一个‘0’的位置进行翻转,如果能转换到最后一个数的话,那么就是成立的,否则输出-1。
其次考虑 x ≠ y 的情况,那么一定存在 x,y 不覆盖的一段使得他们翻转,考虑gcd(x,y)为x与y不覆盖能构造成最小的一段连续区间,通过gcd(x,y) 与 x 和 y组合,可以延伸出很多种区间分法,即裴蜀定理(ax + by = (a,b)),如果能有限次操作次数完成,即有解。
代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 void JiuCherish () { int n,x,y; std::cin >> n >> x >> y; std::string s; std::cin >> s; s = '?' + s; std::vector<int > a (n + 5 ) ; for (int i=1 ;i<=n;i++) a[i] = s[i] - '0' ; auto b = a; int g = std::gcd (x,y); std::vector<std::pair<int ,int >> ans; for (int i=1 ;i+g-1 <=n;i++) { if (a[i]) continue ; int l,r; auto b = ans; ans.clear (); if (i + x - 1 <= n) { ans.pb ({i,i + x - 1 }); l = i + g; r = i + x - 1 ; } else { ans.pb ({i + g - 1 - x + 1 ,i + g - 1 }); l = i + g - 1 - x + 1 ; r = i - 1 ; } while (l <= r) { if (r - l + 1 >= y) { ans.pb ({l,l + y - 1 }); l += y; } else if (r + x <= n) { ans.pb ({r + 1 ,r + x}); r += x; } else { ans.pb ({l - x,l - 1 }); l -= x; } } for (auto [l,r] : ans) { for (int i=l;i<=r;i++) a[i] ^= 1 ; b.pb ({l,r}); } ans = b; } for (int i=1 ;i<=n;i++) { if (a[i] == 0 ) { std::cout << -1 << endl; return ; } } std::cout << ans.size () << endl; for (auto [l,r] : ans) std::cout << l << ' ' << r << endl; }
题意: 给你一个数组,需要操作一次:选择一个非空区间,将其中所有元素都乘2,问最小化数组的极差(最大值-最小值)。
数据范围 $$1\leq n\leq 10^5$$
$$1 \leq a_i \leq 10^9$$
题解:模拟,结论 首先统计右移有多少次,如果不能整除6那么说明有一次是特殊的移动。
然后再算左移有多少次。
代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 void JiuCherish () { int n; std::cin >> n; int ans = n / 6 ; if (n % 6 ) ans += 1 ; n = n - 6 ; if (n % 6 == 0 ) { std::cout << ans << endl; return ; } ans += n / 6 ; std::cout << ans << endl; }