校內集訓 9-24
T1
Description
尤里背叛了蘇維埃聯盟!尤里具有心靈控制的能力,可以控制我方的士兵攻擊同伴。為了避免這種情況,斯大林同志要求你合理地排兵佈陣,使得沒有兩個士兵可以互相攻擊。在這個問題裡,你可以認為士兵的攻擊範圍類似於國際象棋中的馬。也即,位置為(x,y)(x,y)的士兵可以攻擊位置為(x±2,y±1)(x±2,y±1)或(x±1,y±2)(x±1,y±2)的士兵。請計算:在 N*M 的棋盤上最多能 放置多少個士兵。(當然,兩個士兵不能在同一個位置)
設
發現當的時候交叉放一定最優
的時候都可以放
的時候的正方形交替放最優
然後就做完了
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 的線段都被已有的線段 覆蓋。
你可以假定:沒有兩條共線的線段具有公共點,沒有重合的線段。
因為題目保證資料隨機
那麼答案顯然很容易達到
直接暴力加上最優性剪枝就過了
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;
}
來考慮一下資料不隨機怎麼做
我們發現這個答案是具有單調性的
意思是存在大十字架就存在小十字架
那麼我們二分十字架的
每次找到長度大於等於的線段
把他們兩端刪掉 即線段
這樣子如果線段存在交集就是合法的
對於線段求交我們可以用掃描線法
每次掃到線段起點就加入,線段終點就刪除
遇到豎著的線段就查詢區間內是否存在橫著的線段
這些操作可以用實現
複雜度
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,