二分

23.7.11 Day 2

二分

 这个二分模板好记欸。

while (l <= r) {
    int mid = (l + r) >> 1;
    if (check(mid)) l = mid + 1,ans = mid;
    else r = mid - 1;
}

Rest The Shades

 前缀和记录,然后单独计算一下两头的线段,也就是蓝色那一段,用总的减不在里面的就可以。还有一点相似比。而且要记得把 \(min\)\(max\) 改成 \(double\)

 还没有过,现在cf寄了,样例过了。

 好好好过了

#include<bits/stdc++.h>
#define kg putchar(' ')
#define ch puts("")
#define wj puts("0.000000000000000")
#define se second
#define fi first
using namespace std;
typedef long long lwl;
typedef pair<int,int> pii;

const int N = 2e5 + 5, inf = 0x3f3f3f3f;
double zero = 1e-8;

int n,Q;
int wl[N],wr[N];
lwl sum[N];
int a,b,wy;

lwl fr(){
    lwl x = 0, flag = 1;
    char t;
    t = getchar();
    while (t < 48 || t > 57){
        if (t == '-') flag = -1;
        t = getchar();
    }
    while (t >= 48 && t <= 57){
        x = x * 10 + t - 48;
        t = getchar();
    }
    return x*flag;
}

void fw(lwl x){
    if (x < 0) putchar('-'),x = -x;
    if (x > 9){
        fw(x / 10);
    }
    putchar(x % 10 + '0');
    return ;
}

int main(){
    wy = fr(),a = fr(),b = fr();
    int len = b - a;
    n = fr();
    for (int i = 1; i <= n; i ++) {
        wl[i] = fr();
        wr[i] = fr();
        sum[i] = sum[i - 1] + wr[i] - wl[i];
    }
    Q = fr();
    int x,y;
    double ans;
    while (Q --) {
        x = fr(),y = fr();
        double k = 1.0 * y / (y + abs(wy));
        
        double dxa = (a - x) * k + x,dxb = (b - x) * k + x;
        
        if (dxb <= wl[1] || dxa >= wr[n]) {
            wj;
            continue;
        }
        
        double L,R;
        int l = 1,r = n;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (wr[mid] <= dxa + zero) l = mid + 1;
            else r = mid - 1;
        }
        L = sum[l];
        L -= min((double)wr[l] - wl[l], wr[l] - dxa);
        
        l = 1,r = n;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (wl[mid] < dxb - zero) l = mid + 1;
            else r = mid - 1;
        }
        R = sum[n] - sum[r - 1];
        R -= min((double)wr[r] - wl[r], dxb - wl[r]);
        
        ans = 1.0 * len * (sum[n] - R - L) / (dxb - dxa);
        printf("%.15f",ans);
        ch;
    }
    return 0;
}

Rain of Fire

 不是杭师大是被codeforces拉黑了吗,今天做了两道codeforces的题,但是codeforces可以登上去的时间只有一个小时不到,到底什么时候可以交啊!

 代码写了,样例叶过了,但是没办法提交不知道对不对,交了之后再贴上来

 好好好过了过了过了,现在每次都争分多秒交 \(codeforces\)

#include<bits/stdc++.h>
#define kg putchar(' ')
#define ch puts("")
#define wj puts("-1")
#define se second
#define fi first
using namespace std;
typedef long long lwl;
typedef pair<int,int> pii;

const int N = 1e3 + 5, inf = 0x3f3f3f3f;

int n;
pii w[N];
map<pii,int> flag;
int h[N];
int id[5];
int a;

lwl fr(){
	lwl x = 0, flag = 1;
	char t;
	t = getchar();
	while (t < 48 || t > 57){
		if (t == '-') flag = -1;
		t = getchar();
	}
	while (t >= 48 && t <= 57){
		x = x * 10 + t - 48;
		t = getchar();
	}
	return x*flag;
}

void fw(lwl x){
	if (x < 0) putchar('-'),x = -x;
	if (x > 9){
		fw(x / 10);
	}
	putchar(x % 10 + '0');
	return ;
}

int min(int a,int b){
	if (a > b) return b;
	return a;
}

int max(int a,int b){
	if (a < b) return b;
	return a;
}

int dis(int i,int j) {
	return max(abs(w[i].fi- w[j].fi),abs(w[i].se - w[j].se));
}

int find(int x) {
	if (h[x] != x) h[x] = find(h[x]);
	return h[x];
}

bool check(int k) {
	for (int i = 1; i <= n; i ++) {
		h[i] = i;
	}
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j < i; j ++) {
			if (w[i].fi != w[j].fi && w[i].se != w[j].se)
				continue;
			if (dis(i,j) > k) continue;
			int ha = find(i),hb = find(j);
			if (ha != hb) {
				h[ha] = hb;
			}
		}
	}
	int cnt = 0;
	for (int i = 1; i <= n; i ++) {
		if (i == find(i)) {
			cnt ++;
			id[cnt] = i;
		}
	}
	if (cnt == 1) return true;
	if (cnt > 4) return false;
	if (cnt == 2) {
		for (int i = 1; i <= n; i ++) {
			for (int j = 1; j < i; j ++) {
				if (find(i) == find(j)) continue;
				if (dis(i,j) <= k)
						return true;
				if (w[i].se == w[j].se && 
					abs(w[i].fi - w[j].fi) <= 2 * k)
						return true;
				if (w[i].fi == w[j].fi &&
					abs(w[i].se - w[j].se) <= 2 * k)
						return true;
			}
		}
		return false;
	}
	if (cnt == 3) {
		int u[4] = {0,1,2,3};
		for (int qwq = 1; qwq <= 3; qwq ++) {
			swap(u[1],u[qwq]);
			a ++;
			for (int i = 1; i <= n; i ++) {
				if (find(i) != id[u[1]]) continue;
				for (int j = 1; j <= n ; j ++) {
					if (find(j) != id[u[2]]) continue;
					if (dis(i,j) <= k) {
						flag[{w[i].fi,w[j].se}] = a;
						flag[{w[j].fi,w[i].se}] = a;
					}
				}
			}
			for (int i = 1; i <= n; i ++) {
				if (find(i) != id[u[1]]) continue;
				for (int j = 1; j <= n; j ++) {
					if (find(j) != id[u[3]]) continue;
					if (dis(i,j) > k) continue;
					if (flag[{w[i].fi,w[j].se}] == a ||
						flag[{w[j].fi,w[i].se}] == a)
						return true;
				}
			}
		}
		return false;
	}
	if (cnt == 4) {
		int u[5] = {0,1,2,3,4};
		for (int qwq = 1; qwq <= 2; qwq ++) {
			a ++;
			swap(u[2],u[3]);
			for (int i = 1; i <= n; i ++) {
				if (find(i) != id[u[1]]) continue;
				for (int j = 1; j <= n; j ++) {
					if (find(j) != id[u[2]]) continue;
					if (dis(i,j) <= k) {
						flag[{w[i].fi,w[j].se}] = a;
						flag[{w[j].fi,w[i].se}] = a;
					}
				}
			}
			for (int i = 1; i <= n; i ++) {
				if (find(i) != id[u[3]]) continue;
				for (int j = 1; j <= n; j ++) {
					if (find(j) != id[u[4]]) continue;
					if (dis(i,j) > k) continue;
					if (flag[{w[i].fi,w[j].se}] == a ||
						flag[{w[j].fi,w[i].se}] == a)
						return true;
				}
			}
		}
		return false;
	}
	return false;
}

int main(){
	n = fr();
	for (int i = 1; i <= n; i ++) {
		w[i].fi = fr();
		w[i].se = fr();
	}
	for (int i = 1; i <= n; i ++) {
		h[i] = i;
	}
	int cnt = n;
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j < i; j ++) {
			if (w[i].fi != w[j].fi && w[i].se != w[j].se)
				continue;
			int ha = find(i),hb = find(j);
			if (ha != hb) {
				cnt --;
				h[ha] = hb;
			}
		}
	}
	if (cnt > 2) {
		wj;
		return 0;
	}
	int l = 0,r = 2e9 + 1;
	while (l <= r) {
		int mid = ((lwl)l + r) / 2;
		if (check(mid)) r = mid - 1;
		else l = mid + 1;
	}
	fw(l);
	return 0;
}

练习

 三道二分,真是二分复建练习。考的时候只做了两道,还有一道是看了 Richard_H 之后写的,最后一题骗分骗了7分

 今天有自己的笔记本可以登QQ,群里聊天聊飞了。最后一题太长了而且也没有什么时间看了,就输出了个样例然后随便输了个数,有七分的高分!

A.奇怪的矩阵

 因为询问的时候都是以列作为单位的,所以说一开始先将所有数排序并且去重,我用的是 \(set\) ,因为不太习惯用 \(unique\)

 然后算这个去重完的数组差分,也就是相邻的两个数之间的距离,也就是说这两个数经过一段距离后数字会重合。然后再对差分数组排个序,最后对这个差分数组求个前缀和,这样后面直接用就可以了。这里的前缀和就是如果距离超过了当前的差分值,那么这些行提供的贡献就是固定的 \(sum\)

 最后对于每次询问,都把它转化成区间形式,因为这个区间左右移动时,中间的不同的数的数量是不会变的。然后对于这个区间二分一下当前区间在差分数组的哪个位置,这里的二分就相当于手写 \(lowerbound\)

 最后再针对没有完全包括的区间单独算一下就可以了。

#include<bits/stdc++.h>
#define kg putchar(' ')
#define ch puts("")
using namespace std;
typedef long long lwl;

const int N = 1e5 + 5, inf = 0x3f3f3f3f;
const lwl m = 1e18;

int n;
lwl w[N];
map<lwl,lwl> h;
lwl cf[N];
lwl sum[N];

lwl fr(){
	lwl x = 0, flag = 1;
	char t;
	t = getchar();
	while (t < 48 || t > 57){
		if (t == '-') flag = -1;
		t = getchar();
	}
	while (t >= 48 && t <= 57){
		x = x * 10 + t - 48;
		t = getchar();
	}
	return x*flag;
}

void fw(lwl x){
	if (x < 0) putchar('-'),x = -x;
	if (x > 9){
		fw(x / 10);
	}
	putchar(x % 10 + '0');
	return ;
}

int main(){
	freopen("qwq.in","r",stdin);
	set<int> s;
	n = fr();
	for (int i = 1; i <= n; i ++) {
		s.insert(fr());
	}
	n = s.size();
	int idx = 0;
	for (auto i : s) {
		w[++ idx] = i;
	}
	for (int i = 1; i < n; i ++) {
		cf[i] = w[i + 1] - w[i];
	}
	sort(cf + 1,cf + n);
	for (int i = 1; i <= n; i ++) {
		sum[i] = sum[i - 1] + cf[i];
	}
	int Q = fr();
	while (Q --) {
		lwl l = fr(),r = fr();
		lwl y = r - l + 1;
		l = 1,r = idx;
		while (l <= r) {
			lwl mid = (l + r) >> 1;
			if (cf[mid] < y) l = mid + 1;
			else r = mid - 1;
		}
		if (r == n) r = n - 1;
		lwl ans = (n - r) * y + sum[r];
		fw(ans);
		ch;
	}
	return 0;
}

B.区间加法

 考虑贪心加二分。二分就是二分答案,然后这里二分答案的时候不能直接用 \(1e14\) ,不然会 \(TLE\) ,因为这里给的范围是 \(\sum\) ,所以我们对每一组数据都求一遍最大值。

 在 \(check\) 函数里面,求当前答案是否符合的时候,这里用的是树状数组,但是复杂度好像有点问题,可以直接标记用前缀和做,但还我懒得写了,如果改做法可以省掉一个 \(\log n\)

 但是这一题的数据好像不是很强,如果最大值取 \(1e8\) 都可以做,真是让人大吃一惊。

#include<bits/stdc++.h>
#define kg putchar(' ')
#define ch puts("")
#define wj puts("-1")
#define se second
#define fi first
using namespace std;
typedef long long lwl;
typedef pair<int,int> pii;

const int N = 2e5 + 100, inf = 0x3f3f3f3f;

priority_queue<int> q;
int n,m,k,b;
lwl w[N];
pii qj[N];
lwl tr[N];

lwl fr(){
	lwl x = 0, flag = 1;
	char t;
	t = getchar();
	while (t < 48 || t > 57){
		if (t == '-') flag = -1;
		t = getchar();
	}
	while (t >= 48 && t <= 57){
		x = x * 10 + t - 48;
		t = getchar();
	}
	return x*flag;
}

void fw(lwl x){
	if (x < 0) putchar('-'),x = -x;
	if (x > 9){
		fw(x / 10);
	}
	putchar(x % 10 + '0');
	return ;
}

lwl lowbit(lwl a) {
	return a & -a;
}

void add(int x,lwl k) {
	while (x <= n + 1) {
		tr[x] += k;
		x += lowbit(x);
	}
}

lwl sum(int x) {
	lwl ans = 0;
	while (x) {
		ans += tr[x];
		x -= lowbit(x);
	}
	return ans;
}

void change(int l,int r) {
	add(l,b);
	add(r + 1,-b);
}

bool check(lwl mid) {
	while (q.size()) q.pop();
	memset(tr,0,sizeof tr);
	queue<int> qq;
	for (int i = 1; i <= n; i ++) {
		if (w[i] < mid) qq.push(i);
	}
	int cnt = 0;
	int u = 1;
	while (qq.size()) {
		auto t = qq.front();
		qq.pop();
		
		while (u <= m && qj[u].fi <= t) {
			q.push(qj[u].se);
			u ++;
		}
		
		while (w[t] + sum(t) < mid) {
			cnt ++;
			if (cnt > k) return false;
			if (!q.size()) return false;
			
			auto tt = q.top();
			q.pop();
			
			change(t,tt);
		}
	}
	return true;
}

int main(){
	//freopen("qwq.in","r",stdin);
	int T = fr();
	while (T --) {
		n = fr(),m = fr(),k = fr(),b = fr();
		for (int i = 1; i <= n; i ++) {
			w[i] = fr();
		}
		for (int i = 1; i <= m; i ++) {
			qj[i].fi = fr();
			qj[i].se = fr();			
		}
		sort(qj + 1,qj + 1 + m);
		lwl l = 0,r = 1e9;
		while (l <= r) {
			lwl mid = (l + r) >> 1;
			if (check(mid)) l = mid + 1;
			else r = mid - 1;
		}
		fw(r);
		ch;
	}
	return 0;
}

C.无想の一刀

 题意是将一张网格图,分成两个梯形一样的区域,就像是这个样子。

 然后要求这两个区域的极差尽可能小,所以考虑二分求最小值。因为这个显然是有单调性的,所以直接二分答案。

 主要是这个 \(check\) 函数里面该怎样写。通过这个图可以看出,这个形状肯定是以一个角为起点延伸的,而且这个角作为起点的话,他的对角就一定是另一半的起点,所以枚举的时候枚举两个上面的角就可以了。

 显然的,整个图的 \(maxn\)\(minn\) 肯定在这个的两边,所以我们不妨用这两个点作为基准,两个角都遍历一遍作为最小值和最大值的情况。

 在遍历的时候,如果这一条比面一条要短,那么下面一条不能超过这一条的界限,不然就不满足这个形状了。在判断另一个的时候我是把所有这上面的都标记一下,然后下面的一个个看符不符合条件。

int n,m;
int w[N][N];
bool flag[N][N];
int maxn = -inf,minn = inf;

bool check(int mid) {
	memset(flag,0,sizeof flag);
	int lim = m;
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= lim; j ++) {
			if (w[i][j] < maxn - mid) {
				lim = j - 1;
				break;
			}
			flag[i][j] = true;
		}
	}
	bool st = true;
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= m; j ++) {
			if (flag[i][j]) continue;
			if (w[i][j] > mid + minn) {
				st = false;
				break;
			}
		}
		if (!st) break;
	}
	if (st) return st;
	
	
	memset(flag,0,sizeof flag);
	lim = m;
	st = true;
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= lim; j ++) {
			if (w[i][j] > minn + mid) {
				lim = j - 1;
				break;
			}
			flag[i][j] = true;
		}
	}
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= m; j ++) {
			if (flag[i][j]) continue;
			if (w[i][j] < maxn - mid){
				st = false;
				break;
			}
		}
		if (!st) break;
	}
	if (st) return true;
	
	
	memset(flag,0,sizeof flag);
	lim = 1;
	for (int i = 1; i <= n; i ++) {
		for (int j = m; j >= lim; j --) {
			if (w[i][j] < maxn - mid) {
				lim = j + 1;
				break;
			}
			flag[i][j] = true;
		}
	}
	st = true;
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= m; j ++) {
			if (flag[i][j]) continue;
			if (w[i][j] > mid + minn) {
				st = false;
				break;
			}
		}
		if (!st) break;
	}
	if (st) return st;
	
	
	memset(flag,0,sizeof flag);
	lim = 1;
	for (int i = 1; i <= n; i ++) {
		for (int j = m; j >= lim; j --) {
			if (w[i][j] > minn + mid) {
				lim = j + 1;
				break;
			}
			flag[i][j] = true;
		}
	}
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= m; j ++) {
			if (flag[i][j]) continue;
			if (w[i][j] < maxn - mid)
				return false;
		}
	}
	return true;
}

int main(){
	//freopen("qwq.in","r",stdin);
	n = fr(),m = fr();
	int l = 0,r = -inf;
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= m; j ++) {
			w[i][j] = fr();
			r = max(r,w[i][j]);
			maxn = max(maxn,w[i][j]);
			minn = min(minn,w[i][j]);
		}
	}
	int ans = 0;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (check(mid)) ans = mid,r = mid - 1;
		else l = mid + 1;
	}
	fw(ans);
	return 0;
}

中间有很多代码都是重复的。

posted @ 2023-07-11 18:23  jingyu0929  阅读(53)  评论(0)    收藏  举报