校内集训 9-24

版权声明:_ https://blog.csdn.net/lunch__/article/details/82831037

T1

Description

尤里背叛了苏维埃联盟!尤里具有心灵控制的能力,可以控制我方的士兵攻击同伴。为了避免这种情况,斯大林同志要求你合理地排兵布阵,使得没有两个士兵可以互相攻击。在这个问题里,你可以认为士兵的攻击范围类似于国际象棋中的马。也即,位置为(x,y)(x,y)的士兵可以攻击位置为(x±2,y±1)(x±2,y±1)或(x±1,y±2)(x±1,y±2)的士兵。请计算:在 N*M 的棋盘上最多能 放置多少个士兵。(当然,两个士兵不能在同一个位置)

n < m n < m

发现当 n > 3 n > 3 的时候交叉放一定最优

n = 1 n = 1 的时候都可以放

n = 2 n = 2 的时候 2 × 2 2×2 的正方形交替放最优

然后就做完了

Codes

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

void File() {
	freopen("psycho.in", "r", stdin);
	freopen("psycho.out", "w", stdout);
}

void Solve() {
	int T, n, m;
	for(scanf("%d", &T); T -- ; ) {
		scanf("%d%d", &n, &m);
		if(n > m) swap(n, m);
		if(n == 1) {printf("%d\n", m); continue;}
		if(n == 2) {printf("%d\n", 4 * (((m / 2) + 1) / 2) + ((!((m / 2) & 1)) & (m & 1)) * 2); continue;}
		int n1 = n / 2, n2 = n - n1, m1 = m / 2, m2 = m - m1;
		printf("%lld\n", (ll)m2 * n2 + (ll)m1 * n1);
	}
}

int main() {
	//File();
	Solve();
	return 0;
}

T2

Description

“我是超级大沙茶”—— MatoNo1MatoNo1
为了证明自己是一个超级大沙茶,MatoMato 神犇决定展示自己对叉(十字型)有多么的了解。

MatoMato 神犇有一个平面直角坐标系,上面有一些线段,保证这些线段至少与一条坐标轴平行。MatoMato 神犇需要指出,这些线段构成的最大的十字型有多大。

称一个图形为大小为 RR(RR 为正整数)的十字型,当且仅当,这个图形具有一个中心点,它存在于某一条线段上,并且由该点向上下左右延伸出的长度为 RR 的线段都被已有的线段 覆盖。

你可以假定:没有两条共线的线段具有公共点,没有重合的线段。

因为题目保证数据随机

那么答案显然很容易达到

直接 O ( n 2 ) O(n^2) 暴力加上最优性剪枝就过了

Codes

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int n, ch, cs, ans;

struct node {
	int _x1, _y1, _x2, _y2;
	bool operator < (const node &T) const {
		return _x1 < T._x1;
	}
}heng[N], shu[N], tmp;

void File() {
	freopen("cross.in", "r", stdin);
	freopen("cross.out", "w", stdout);
}

int read() {
	int _ = 0, __ = getchar(), ___ = 1;
	for(; !isdigit(__); __ = getchar()) if(__ == '-') ___ = -1;
	for(; isdigit(__); __ = getchar()) _ = (_ << 3) + (_ << 1) + (__ ^ 48);
	return _ * ___;
}

void Solve() {
	int _x1, _x2, _y1, _y2, l, r; n = read();
	for(int i = 1; i <= n; ++ i) {
		_x1 = read(), _y1 = read(), _x2 = read(), _y2 = read();
		if(_x1 > _x2) swap(_x1, _x2);
		if(_y1 > _y2) swap(_y1, _y2);
		if(_x1 == _x2) shu[++ cs] = node{_x1, _y1, _x2, _y2};
		else heng[++ ch] = node{_x1, _y2, _x2, _y2};
	}
	if(!cs || !ch) return (void)puts("Human intelligence is really terrible");
	sort(shu + 1, shu + cs + 1);	
	for(int i = 1; i <= ch; ++ i) {
		tmp._x1 = heng[i]._x1; l = lower_bound(shu + 1, shu + cs + 1, tmp) - shu;
		tmp._x1 = heng[i]._x2; r = upper_bound(shu + 1, shu + cs + 1, tmp) - shu;
		//cout << l << ' ' << r << endl;
		int len = (heng[i]._x2 - heng[i]._x1) / 2;
		for(int j = l; j < r; ++ j) {
			if(ans >= len) break;
			if(heng[i]._x1 <= shu[j]._x1 && shu[j]._x1 <= heng[i]._x2)	
				if(shu[j]._y1 <= heng[i]._y1 && heng[i]._y1 <= shu[j]._y2) {
					int th = min(heng[i]._y1 - shu[j]._y1, shu[j]._y2 - heng[i]._y1);
					int ts = min(shu[j]._x1 - heng[i]._x1, heng[i]._x2 - shu[j]._x1);
					ans = max(ans, min(th, ts));
				}
		}
	}
	if(ans) printf("%d\n", ans);
	else puts("Human intelligence is really terrible");
}

int main() {
//	File();
	Solve();
	return 0;
}

来考虑一下数据不随机怎么做

我们发现这个答案是具有单调性的

意思是存在大十字架就存在小十字架

那么我们二分十字架的 R R

每次找到长度大于等于 2 R 2R 的线段

把他们两端删掉 即线段 [ x , y ] &gt; [ x + m i d , y m i d ] [x, y] -&gt;[x + mid, y - mid]

这样子如果线段存在交集就是合法的

对于线段求交我们可以用扫描线法

每次扫到线段起点就加入,线段终点就删除

遇到竖着的线段就查询区间内是否存在横着的线段

这些操作可以用 m u l t i s e t multiset 实现

复杂度 O ( n l o g n l o g L ) O(nlognlogL)

Codes

#include<bits/stdc++.h>

using namespace std;

const int N = 4e5 + 10;

int n, ch, cs, cnt;

struct Seg {
	int x, y, _y, len;
}H[N], S[N];

struct node {
	int id, pos, L, R;
	bool operator < (const node &T) const {
		return pos == T.pos ? id < T.id : pos < T.pos;
	}
}tmp[N];

multiset<int> T;

bool check(int now) {
	for(int i = 1; i <= ch; ++ i) 
		if(H[i].len >= now * 2) 
			tmp[++ cnt] = node{1, H[i].y + now, H[i].x, 0}, 
			tmp[++ cnt] = node{3, H[i]._y - now, H[i].x, 0};
	for(int i = 1; i <= cs; ++ i) 
		if(S[i].len >= now * 2) 
			tmp[++ cnt] = node{2, S[i].x, S[i].y + now, S[i]._y - now};
	sort(tmp + 1, tmp + cnt + 1);
	for(int i = 1; i <= cnt; ++ i) {
		if(tmp[i].id == 1) T.insert(tmp[i].L);
		if(tmp[i].id == 3) T.erase(T.find(tmp[i].L));
		if(tmp[i].id == 2 && T.upper_bound(tmp[i].R) != T.lower_bound(tmp[i].L)) return true;
	}
	return false;
}

int main() {
#ifdef ylsakioi
	freopen("cross.in", "r", stdin);
	freopen("cross.out", "w", stdout);
#endif
	int x, _x, y, _y; scanf("%d", &n);
	for(int i = 1; i <= n; ++ i) {
		scanf("%d%d%d%d", &x, &y, &_x, &_y);
		if(x > _x) swap(x, _x); if(y > _y) swap(y, _y);
		if(x == _x) S[++ cs] = Seg{x, y, _y, _y - y};
		if(y == _y) H[++ ch] = Seg{y, x, _x, _x - x};
	}
	int L = 0, R = 1e9, ans = 0;
	while(cnt = 0, T.clear(), L <= R) {
		int mid = (L + R) >> 1;
		if(check(mid)) ans = mid, L = mid + 1;
		else R = mid - 1;
	}
	if(ans) printf("%d\n", ans);
	else puts("Human intelligence is really terrible");
	return 0;
}

T2

Description

从前有一个 RudyRudy。

从前还有一个网格图。

RudyRudy 喜欢爆炸。

RudyRudy 偶尔会炸掉网格图中的一条边(u,v)(u,v)。之后他会尝试从 uu 走到 vv。

如果他成功地从 uu 走到 vv,他会很高兴;否则他会找人打架。

从第二次爆炸开始,根据 RudyRudy 此时心情的不同,RudyRudy 会炸掉不同的边。

你被要求编写一个程序,对于每次爆炸,给出此时 RudyRudy 是否还能从 uu 到 vv。

把网格图的点变成格子

删一条边相当于让两个格子联通

如果两个格子在删这条边前就联通那就无解了

否则就有解 把格子外面也当做一个大格子就可以了

用并查集维护即可

Codes

#include<bits/stdc++.h>

using namespace std;

const int N = 500 + 10;

int flag, fa[N * N], n, q;

void File() {
	freopen("babystep.in", "r", stdin);
	freopen("babystep.out", "w", stdout);
}

int read() {
	int _ = 0, __ = getchar(), ___ = 1;
	for(; !isdigit(__); __ = getchar()) if(__ == '-') __ = -1;
	for(; isdigit(__); __ = getchar()) _ = (_ << 3) + (_ << 1) + (__ ^ 48);
	return _ * ___;
}

int find(int x) {
	return fa[x] = x == fa[x] ? x : find(fa[x]);
}

bool merge(int x, int y) {
	//cout << x << ' ' << y << endl;
	x = find(x), y = find(y);
	if(x == y) return false;
	return fa[x] = y, true;
}

void get_flag(int _x1, int _y1, int _x2, int _y2) {
	//cout << _x1 << ' ' << _y1 << ' ' << _x2 << ' ' << _y2 << endl;
	if(_x1 == _x2 && _x1 == 1) flag = merge((n - 1) * (n - 1) + 1, _y1);
	else if(_x1 == _x2 && _x1 == n) flag = merge((n - 1) * (n - 1) + 1, (n - 1) * (n - 2) + _y1);
	else if(_y1 == _y2 && _y1 == 1) flag = merge((n - 1) * (n - 1) + 1, (_x1 - 1) * (n - 1) + 1);
	else if(_y1 == _y2 && _y2 == n) flag = merge((n - 1) * (n - 1) + 1, _x1 * (n - 1));
	else if(_x1 == _x2) flag = merge((n - 1) * (_x1 - 2) + _y1, (n - 1) * (_x1 - 1) + _y1);
	else if(_y1 == _y2) flag = merge((n - 1) * (_x1 - 1) + _y1 - 1, (n - 1) * (_x1 - 1) + _y2);
}

void Solve() {
	int _x1, _y1, _x2, _y2; 
	n = read(), q = read();
	for(int i = 1; i <= n * n; ++ i) fa[i] = i;
	_x1 = read(), _y1 = read(), _x2 = read(), _y2 = read();
	if(_x1 > _x2) swap(_x1, _x2); if(_y1 > _y2) swap(_y1, _y2);
	get_flag(_x1, _y1, _x2, _y2); puts(flag ? "HAHA" : "DAJIA");
	for(int cas = 1; cas < q; ++ cas) {
		if(flag) _x1 = read(), _y1 = read(), _x2 = read(), _y2 = read(), read(), read(), read(), read();
		else read(), read(), read(), read(), _x1 = read(), _y1 = read(), _x2 = read(), _y2 = read();
		if(_x1 > _x2) swap(_x1, _x2); if(_y1 > _y2) swap(_y1, _y2);
		get_flag(_x1, _y1, _x2, _y2); puts(flag ? "HAHA" : "DAJIA");
	}
}

int main() {
	Solve();
	return 0;
}

今天又炸飞了… O r z   L s t e t e Orz\ Lstete 吊打 A K   K i n g AK\ King

自己还是太菜了

猜你喜欢

转载自blog.csdn.net/lunch__/article/details/82831037