// 二分的主体代码往往相同,不同点在于check()函数 boolcheck(int e){ for (int i = 1; i <= n; ++ i){ e = e * 2 - h[i]; if (e >= 1e5) returntrue; // 没有这一步会爆int // check()函数中能尽早判断出是否满足性质就要尽早 // 因而要充分研究问题性质,用数学方法放缩出一些显然成立的情况,简化问题 if (e < 0) returnfalse; } returntrue; }
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; ++ i) scanf("%d", &h[i]);
int l = 0, r = 1e5; // 二分的一大关键点在于恰当地确定最初地区间端点
while (l < r){ int mid = l + r >> 1; if (check(mid)) r = mid; else l = mid + 1; } printf("%d\n", r); return0; }
# AcWing 1227. 分巧克力 # 二分 defmain(): n, k = map(int, input().split()) h, w = [0] * n, [0] * n for i inrange(n): h[i], w[i] = map(int, input().split()) defcheck(mid): cnt = 0 for i inrange(n): cnt += (h[i] // mid) * (w[i] // mid) return cnt >= k l, r = 0, max(h) while l < r: mid = l + r + 1 >> 1 if check(mid): l = mid else: r = mid - 1 print(r) if __name__ == "__main__": main()
structSum{ int s, c, d; // 有多个关键字的排序,按主关键字序排序 bool operator< (const Sum &t)const{ if (s != t.s) return s < t.s; // 一定注意 if 语句里要用!=不等号而不能用小于号 if (c != t.c) return c < t.c; return d < t.d; } }sum[N];
int n, m;
intmain(){ scanf("%d", &n);
for (int c = 0; c * c <= n; ++ c) for (int d = c; c + d * d <= n; ++ d) sum[m ++ ] = {c * c + d *d , c , d}; // 打表
sort(sum, sum + m);
for (int a = 0; a * a <= n; ++ a) for (int b = 0; a * a + b * b <= n; ++ b){ int t = n - a * a - b * b; int l = 0, r = m - 1; while (l < r){ int mid = l + r >> 1; if (sum[mid].s >= t) r = mid; else l = mid + 1; } if (sum[l].s == t){ printf("%d %d %d %d\n", a, b , sum[l].c, sum[l].d); return0; } } return0; }
boolcheck(int t){ int l = 0; for (int i = 0; i < k; i++) { if (s[i] - t <= l) { if (s[i] <= l) l = s[i] + t - 1; else l += t; } else returnfalse; } if (l >= n) returntrue; else returnfalse; }
intmain() { scanf("%d%d", &n , &k);
for (int i = 0; i < k; ++ i) scanf("%d", &s[i]); sort(s, s + k); int l = 1, r = n; while (l < r){ int mid = l + r >> 1; if (check(mid)) r = mid; else l = mid + 1; } printf("%d\n", 2 * (l - 1)); return0; }
最后,所有的硬币都是向上的,但是 “聪明” 的小 M 说了,如果再按照 Q 操作玩一次,就可以恢复原状,现在问原来有多少硬币是向下的。明确几点: 1. 从最后开始,先不管 P 操作究竟是怎么样的,只要某个硬币,被翻得次数是奇数次的,那么肯定要变成向下的。 2. 接下来请仔细看 P 操作,对 i × x 和 j × y,注意是 “所有的”,也就是说只要你这个 i*x 不超过最大的 n,那么所有可能的i都得取到。比如说现在最大的 n 是 20,x 现在是 2,那么 i 必须要取 2,3,4,5,6,7,8,9,10, 意思是所有在 n 范围内 x 的倍数都得取到。那么同理,所有在 m 范围内 y 的倍数也必须取到。 3. 明确了第二点,那么分析,对于某个点,假设坐标为(a,b),那么这个点被翻过的次数就是 a 的约数个数 × b 的约数个数。 4. 明确了第三点,那么想找到被翻过次数是奇数个的点,那么 a 和 b 的约数个数的乘积必须是是个奇数,如果 a 和 b 约数个数乘积必须是奇数,那么 a 和 b 他俩任何一个约数的个数都不能有 2,也就意味着,a 和 b 的约数个数都必须也是奇数乘出来才可能是奇数。 5. 明确了第四点,现在要寻找,什么样的数,才有奇数个约数呢?答案是——完全平方数!比如 0,4,9,16,25, 等等这些数字是完全平方数,也就是某个自然数的平方。那么也就是要求 a 和 b 都得是完全平方数。那么一个自然数,究竟包括了多少个完全平方数(不含 0,因为本题要求坐标从 1 开始),需要对这个数开方取整即可。比如 10,开方之后为 3. 几,意思是他包含了 3 个完全平方数,就是 1 4 9。 6. 最后,问题就转化成求 n 和 m 包括的完全平方数的个数再相乘,也就是根号 n 乘 根号 m 的结果,注意先取整再计算。
查询时从树的根节点出发,先查询左子树和(记为sum),比较 k 和 sum 的大小关系:若 k≤sum 则说明第 k 小数在左子树中,递归查询左子树;否则,这个数对应的就是右子树中第 k−sum 小的数,令 k−=sum,然后递归查询右子树。
时间复杂度 O(nlogn),空间复杂度 O(n)。
Q:如果是静态(即没有对序列的数进行修改)求某个区间的第 k 大怎么做呢?
A:建立 n 棵前缀权值线段树,那么任意一段区间均可以用两棵权值线段树作差来表示,即区间 [L,R] 的信息可以由第 R 棵权值线段树 - 第 L 棵权值线段树得到。不过每个前缀开一棵权值线段树空间复杂度 O(n2),无法开出这么大空间,而考虑到后一个位置相比于前一个位置的更改只有 logn 个节点,所以使用主席树来优化空间。
时间复杂度 O(nlogn) 空间复杂度 O(nlogn)
Q:如果是动态(有对序列的数进行修改)求某个区间的第 k 大怎么做呢(本题)?
A:还是要想办法维护前缀和。
我们需要去更新 $tree[x],tree[x+1],…,tree[n] $的信息。更新一个 tree 需要的时间复杂度 O(log),而 x∼n 最坏有 n 个 tree 需要更新,那么更新的总时间复杂度为 O(nlog),而这只是一次操作。而本题一共有 m 次操作,如果每次操作都是最坏的情况,那么时间复杂度为$ O(NMlogN)$ ,显然会超时。那有什么办法可以快速维护前缀信息呢?不难想到树状数组。
`#include<bits/stdc++.h> usingnamespace std; constint N = 1e5 + 10; intlowbit(int x){ return x & (-x) ; } int n , m , tot = 1 , qid = 1; structQuery { int x , y , k , id , ch; } q[N] , q1[N] , q2[N]; int a[N] , ans[N]; structBIT { int tree[N]; int maxn; voidinit(int n) { memset(tree , 0 , sizeof tree); maxn = n; } voidadd(int pos , int val) { for(int i = pos ; i <= maxn ; i += lowbit(i)) { tree[i] += val; } } intask(int pos) { int ans = 0; for(int i = pos ; i ; i -= lowbit(i)) ans += tree[i]; return ans; } } bit;
voidsolve(int ql,int qr,int l,int r) { if(ql > qr) return ; if(l == r) { for(int i = ql ; i <= qr ; i ++) if(q[i].ch == 2) ans[q[i].id] = l; return ; } int mid = l + r >> 1; int L = 0 , R = 0; for(int i = ql ; i <= qr ; i ++) { if(q[i].ch == 1) { if(q[i].x <= mid) { bit.add(q[i].id , q[i].y); q1[L ++] = q[i]; } else q2[R ++] = q[i]; } else { int res = bit.ask(q[i].y) - bit.ask(q[i].x - 1); if(res >= q[i].k) q1[L ++] = q[i]; else q[i].k -= res , q2[R ++] = q[i]; } } for(int i = 0 ; i < L ; i ++) if(q1[i].ch == 1) bit.add(q1[i].id, -q1[i].y); int now = ql; for(int i = 0 ; i < L ; i ++) q[now ++] = q1[i]; for(int i = 0 ; i < R ; i ++) q[now ++] = q2[i]; solve(ql , ql + L - 1 , l , mid); solve(ql + L , ql + L + R - 1 , mid + 1 , r); } signedmain() { cin >> n >> m; bit.init(n); for(int i = 1 ; i <= n ; i ++) q[tot ++] = Query{0 , 1 , 0 , i , 1}; for(int i = 1 ; i <= m ; i ++) { char op; cin >> op; if(op == 'Q') { int l , r; cin >> l >> r; int k = r - l + 1 - 8 + 1; q[tot++]=Query {l,r,k,qid++,2}; } else { int x , y; cin >> x >> y; q[tot ++] = Query{a[x] , -1 , 0 , x , 1}; a[x] = y; q[tot ++] = Query{a[x] , 1 , 0 , x , 1}; } } solve(1 , tot - 1 , 0 , 1e9); for(int i = 1 ; i < qid ; i ++) cout << ans[i] << '\n'; return0; }
boolcheck(int k){ // unordered_map<string, bool> hash; unordered_set<string> hash; for (int i = 0; i + k - 1 < n; ++ i){ string t = s.substr(i, k); // if (hash.find(t) == hash.end()) m[t] = true; // else return false; if (hash.count(t)) returnfalse; else hash.insert(t); // 复杂度为O(n) } returntrue; }
intmain(){ scanf("%d", &n); cin >> s; int l = 1, r = n; while (l < r){ int mid = l + r >> 1; if (check(mid)) r = mid; else l = mid + 1; } printf("%d\n", l); return0; }
ULL get(int l, int r){ return hs[r] - hs[l - 1] * p[r - l + 1]; }
boolcheck(int k){ unordered_set<int> hash; for (int i = 0; i + k - 1 < n; ++ i){ int t = get(i + 1, i + k); // 注意字符串哈希下表下标从1开始,所以这里要加1 if (hash.count(t)) returnfalse; else hash.insert(t); } returntrue; }
intmain(){ scanf("%d", &n); cin >> s; p[0] = 1; for (int i = 1; i <= n; ++ i){ p[i] = p[i - 1] * P; hs[i] = hs[i - 1] * P + s[i - 1]; } int l = 1, r = n; while (l < r){ int mid = l + r >> 1; if (check(mid)) r = mid; else l = mid + 1; } printf("%d\n", l); return0; }
# AcWing 1460. 我在哪?(每日一题) # 暴力 + 集合/哈希 defmain(): n = int(input()) s = input() k, hs = 1, set() for l inrange(1, n + 1): # 枚举长度 flag = True for i inrange(n - l + 1): tmp = s[i:i+l] # 注意切片:后要是末端下标+1 if tmp in hs: # 判断字符串是否在集合中 k = l + 1 flag = False break else: hs.add(tmp) if flag: returnprint(k) if __name__ == "__main__": main()
# AcWing 1460. 我在哪?(每日一题) # 二分 + 集合/哈希 defmain(): n = int(input()) s = input() l, r, hs = 1, n, set() while l < r: mid = l + r >> 1 # 集合中元素数量等于n-mid+1,说明所有元素互不相同,没有重复子串 iflen(set([s[i:i+mid] for i inrange(n - mid + 1)])) == n - mid + 1: r = mid else: l = mid + 1 print(l) if __name__ == "__main__": main()
#include<bits/stdc++.h> usingnamespace std; constint maxn = 20000; int n; structnode { int a; int b; }; vector<node> reg; boolcmp(node x,node y){ return x.b < y.b; } boolcheck(int x){ int k = 0; vector<node> tmp(reg); while(true){ bool found = false; for(int i = 0; i < tmp.size(); i++){ node now = tmp[i]; int ta = now.a; int tb = now.b; if (ta - x <= k && tb + x >= k){ found = true; int len = tb-ta; // if(ta+x>=k) k += len; // else k = tb+x; k += min(len, tb + x - k); tmp.erase(tmp.begin()+i); break; } } if(!found || k>=maxn) break; } return k >= maxn; } intmain() { scanf("%d",&n); for(int i = 0; i < n;i++){ int a, b; scanf("%d%d",&a,&b); a *= 2; b *= 2; reg.push_back({a, b}); } sort(reg.begin(),reg.end(),cmp); int l = 0, r = maxn; while(l < r){ int mid = l + r >> 1; if(check(mid)) r = mid; else l = mid + 1; } double ans = r / 2.0; cout << ans << endl; return0; }
defmain(): n = int(input()) q = sorted([[int(x) * 2for x ininput().split()] for _ inrange(n)], key = lambda x: (x[1]))
defcheck(mid): s = 0 cq = q.copy() whileTrue: flag = False for x in cq: l, r = x[0], x[1] if l - mid <= s <= r + mid: s += min(r - l, r + mid - s) # if q[i][0] + mid >= s: # s += q[i][1] - q[i][0] # else: # s = q[i][1] + mid flag = True cq.remove(x) break ifnot flag or s >= 2e4: return s >= 2e4
l, r = 0, int(2e4) while l < r: mid = l + r >> 1 if check(mid): r = mid else: l = mid + 1 print(l / 2if l % 2 != 0else l // 2) if __name__ == "__main__": main()
defmain(): n = int(input()) q = sorted([list(map(int, input().split())) for _ inrange(n)], key = lambda x: (x[1]))
defcheck(mid): s = 0 cq = q.copy() whileTrue: flag = False for x in cq: l, r = x[0], x[1] if l - mid <= s <= r + mid: s += min(r - l, r + mid - s) # if q[i][0] + mid >= s: # s += q[i][1] - q[i][0] # else: # s = q[i][1] + mid flag = True cq.remove(x) break ifnot flag or s >= 1e4: return s >= 1e4
l, r = 0, 1e4 eps = 1e-3 while r - l > eps: mid = (l + r)/2 if check(mid): r = mid else: l = mid x = (l + r) / 2 ifabs(round(x)-x)<0.05: print(round(x)) else: print(int(round(x*10))/10) if __name__ == "__main__": main()