A. Stones
簽到。
B. Alice and the List of Presents
單獨考慮每個數的貢獻即可。
答案為\((2^{m}-1)^n\)。
C. Labs
構造就類似於:
1 6 7
2 5 8
3 4 9
這樣就行了。
證明我也不會,但感覺這樣能使得每一行都較為均衡。
Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
// #define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 305;
int n;
int mp[N][N];
void run() {
cin >> n;
for(int j = 1; j <= n; j++) {
int st = (j - 1) * n + 1;
if(j & 1) {
for(int i = n; i >= 1; i--, st++) {
mp[i][j] = st;
}
} else {
for(int i = 1; i <= n; i++, st++) {
mp[i][j] = st;
}
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
cout << mp[i][j] << " \n"[j == n];
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
#ifdef Local
freopen("../input.in", "r", stdin);
freopen("../output.out", "w", stdout);
#endif
while(cin >> n) run();
return 0;
}
D. Alice and the Doll
題意:
給出一個\(n\cdot m,n,m\leq 10^5\)大小的網格圖,上面有些位置可能有障礙。
現在要求,每個格子只能經過一次,並且在每個格子有兩個選擇:直走或者右轉。初始位於\((1,1)\)位置,方向為向右。
問能否走遍所有非障礙的點。
思路:
把題讀成一個位置可以經過多次,但只能右轉一次,想了一小時假題
因為只能經過一次,那么我們路線肯定是“回路”型的,並且肯定是貪心地走。
那么我們每行每列存障礙,然后模擬這個過程就行。
代碼中用了一個矩形表示當前范圍。注意每次刪障礙時肯定要刪除一個矩形,矩形內如果有空地那就不合法,因為不可能再走過去了。
注意矩形范圍的更新。
細節見代碼:
Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
// #define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 5;
int n, m, k;
set <int> col[N], row[N];
bool del(int up, int down, int left, int right) {
for(int i = up; i <= down; i++) {
for(int j = left; j <= right; j++) {
if(row[i].find(j) == row[i].end()) return false;
row[i].erase(j);
col[j].erase(i);
--k;
}
}
return true;
}
void run() {
for(int i = 1; i <= k; i++) {
int x, y; cin >> x >> y;
col[y].insert(x);
row[x].insert(y);
}
int dir = 0;
int up = 1, down = n, left = 1, right = m;
int D = 0;
int x = 1, y = 1;
while(k) {
if(dir == 0) {
auto it = row[x].begin();
if(it == row[x].end()) {
y = right;
} else {
int p = *it;
if(!del(up, down, p, right)) {
cout << "No";
return;
}
right = y = p - 1;
}
left += D;
} else if(dir == 1) {
auto it = col[y].begin();
if(it == col[y].end()) {
x = down;
} else {
int p = *it;
if(!del(p, down, left, right)) {
cout << "No";
return;
}
down = x = p - 1;
}
up += D;
} else if(dir == 2) {
auto it = row[x].rbegin();
if(it == row[x].rend()) {
y = left;
} else {
int p = *it;
if(!del(up, down, left, p)) {
cout << "No";
return;
}
left = y = p + 1;
}
right -= D;
} else {
auto it = col[y].rbegin();
if(it == col[y].rend()) {
x = up;
} else {
int p = *it;
if(!del(up, p, left, right)) {
cout << "No" << '\n';
return;
}
up = x = p + 1;
}
down -= D;
}
dir = (dir + 1) % 4;
D = 1;
// cout << up << ' ' << down << ' ' << left << ' ' << right << '\n';
}
cout << "Yes" << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
#ifdef Local
freopen("../input.in", "r", stdin);
freopen("../output.out", "w", stdout);
#endif
while(cin >> n >> m >> k) run();
return 0;
}
E. Alice and the Unfair Game
題意:
現有\(n\)個方格排成一列,同時給出一個序列\(a\),\(a_i\)表示第\(i\)次時敲打\(a_i\)這個方格。
現有個玩偶在這個方格上面走,每次敲打后可以向相鄰方格走一步或者留在原地。並規定一開始可以移動一步。
問存在多少對\((x,y)\),表示玩偶一開始在\(x\),最后在\(y\)位置,並且中間不會被敲打。
思路:
有一個觀察:
- 對於一個點\(x\),若其最遠向右能走到\(y\),那么最終走到\(x\)~\(y\)之間的方格都為合法答案。
- 向左延申同理。
感性證明
證明的話可以感性理解一下,因為在一個時間點只能敲打一次,倒過來考慮,因為最后能夠停留在$y$,假設我們是從$y-1$走過來,那么必然最后一次不會敲打$y-1$這個位置,那直接停在這個位置即可;如果我們一直卡在$y$,那也是可以往前走的。
所以現在問題的關鍵就是對於每個位置,如何快速找到向左、向右延申的最遠位置。
因為這個題跟時間有很大關系,所以我們考慮用二維坐標\((x,y)\)表示在\(x\)時刻位於第\(y\)個方格。那么敲打點就相當於二維平面上的一個障礙。
問題就變為:我們從\((0,y)\)出發,每次可以向上、向右、向下走一步,不能經過障礙,能走到的最上/最下的位置是多少。
以最大舉例:我們采用貪心的策略,用vector存儲同一斜率上的所有障礙點,然后二分找到障礙點位置\((x_i,y_i)\),那我們只能走到\((x_i,y_i-1)\);之后快速找到下一個\(y_j\not ={y_i}\)的時間點(預處理下一個位置),走過去即可。
詳細細節見代碼:
Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
// #define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e5 + 5, base = 1e5;
int n, m;
int a[N], nxt[N];
vector<int> v1[N], v2[N];
int Max[N], Min[N];
void run() {
for(int i = 1; i < N; i++) v1[i].clear(), v2[i].clear();
for(int i = 1; i <= m; i++) {
int x; cin >> x;
v1[i - x + base].push_back(i);
v2[i + x].push_back(i);
a[i] = x;
}
if(n == 1 && *max_element(a + 1, a + m + 1) == 1) {
cout << 0 << '\n';
return;
}
nxt[m] = m + 1;
for(int i = m - 1; i >= 1; i--) {
if(a[i] == a[i + 1]) nxt[i] = nxt[i + 1];
else nxt[i] = i + 1;
}
for(int i = 1; i <= n; i++) {
int x = 0, y = i;
while(1) {
int k = x - y + base;
auto it = lower_bound(v1[k].begin(), v1[k].end(), x + 1);
if(it == v1[k].end()) {
y += m + 1 - x;
break;
}
int p = it - v1[k].begin();
p = v1[k][p];
x = nxt[p] - 1, y = a[p] - 1;
}
Max[i] = min(y, n);
}
for(int i = 1; i <= n; i++) {
int x = 0, y = i;
while(1) {
int k = x + y;
auto it = lower_bound(v2[k].begin(), v2[k].end(), x + 1);
if(it == v2[k].end()) {
y -= m + 1 - x;
break;
}
int p = it - v2[k].begin();
p = v2[k][p];
x = nxt[p] - 1, y = a[p] + 1;
}
Min[i] = max(1, y);
}
ll ans = 0;
for(int i = 1; i <= n; i++) {
ans += (Max[i] - Min[i] + 1);
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
#ifdef Local
freopen("../input.in", "r", stdin);
freopen("../output.out", "w", stdout);
#endif
while(cin >> n >> m) run();
return 0;
}
注意一下,當\(n>1\)時,每個位置肯定至少存在一個解。但是當\(n=1\)時,因為沒有周轉的地方,所以可能沒有解,所以我們需要特判一下。