# Sol BN03
## A. Lợi và lợn
### Sub 2:
$f[i][j][u]$ là số cách đi mà thằng bên trái đang ở ($i, j$) và thằng bên phải đang ở hàng $u$ (vì từ $3$ biến này có thể suy ra cột của thằng bên phải).
```cpp=
#include <bits/stdc++.h>
#define VanLoi ""
#define gb(i, j) ((i >> j) & 1)
using namespace std;
const int N = (int)500 + 5, MOD = (int)1e9 + 7, mod = (int)1e9 + 20041203;
int n, m, f[205][205][205];
char a[N][N];
void add(int& x, int y) {
x += y;
if (x >= MOD) x -= MOD;
}
int ci[2] = { 0, 1 };
int di[2] = { 1, 0 };
bool inside(int i, int j) {
if (1 <= i && i <= n && 1 <= j && j <= m) return true;
return false;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
#ifndef ONLINE_JUDGE
freopen(VanLoi".inp", "r", stdin);
freopen(VanLoi".out", "w", stdout);
#endif
cin >> n >> m;
int res = 0;
int diagonal = n + m - 1;
int pos = (diagonal - 1) / 2 + 2;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) cin >> a[i][j];
if (a[1][1] == a[n][m]) f[1][1][n] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
for (int u = 1; u <= n; u++) {
int v = n + m + 2 - i - j - u;
if (v < 1 || v > m) continue;
if (f[i][j][u] == 0) continue;
for (int t = 0; t <= 1; t++) {
int new_i = i + ci[t];
int new_j = j + di[t];
if (!inside(new_i, new_j)) continue;
for (int tt = 0; tt <= 1; tt++) {
int new_u = u - ci[tt];
int new_v = v - di[tt];
if (!inside(new_u, new_v)) continue;
if (a[new_i][new_j] == a[new_u][new_v]) add(f[new_i][new_j][new_u], f[i][j][u]);
}
}
}
}
if (diagonal & 1) {
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) if (i + j == pos) add(res, f[i][j][i]);
}
else {
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) if (i + j == pos) {
for (int t = 0; t <= 1; t++) {
int new_i = i + ci[t];
int new_j = j + di[t];
if (inside(new_i, new_j) && a[i][j] == a[new_i][new_j]) {
add(res, f[i][j][new_i]);
}
}
}
}
cout << res;
}
```
### Sub 3:
Xử lí theo từng đường chéo từ ngoài vào trong hoặc từ trong ra ngoài.
```cpp=
#include <bits/stdc++.h>
#define VanLoi ""
#define gb(i, j) ((i >> j) & 1)
using namespace std;
const int N = (int)500 + 5, MOD = (int)1e9 + 7, mod = (int)1e9 + 20041203;
int n, m, f[N][N], g[N][N], res;
char a[N][N];
vector <int> list_i[N * 2];
void add(int& x, int y) {
x += y;
if (x >= MOD) x -= MOD;
}
int ci[2] = { 0, 1 };
int di[2] = { 1, 0 };
bool inside(int i, int j) {
if (1 <= i && i <= n && 1 <= j && j <= m) return true;
return false;
}
void process(int i, int j, int u, int v) {
for (int le = 0; le <= 1; le++) {
int new_i = i + ci[le];
int new_j = j + di[le];
if (inside(new_i, new_j)) {
for (int ri = 0; ri <= 1; ri++) {
int new_u = u - ci[ri];
int new_v = v - di[ri];
if (inside(new_u, new_v) && a[new_i][new_j] == a[new_u][new_v]) {
add(g[new_i][new_u], f[i][u]);
}
}
}
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n >> m;
int diagonal = n + m - 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
list_i[i + j].push_back(i);
}
if (a[1][1] != a[n][m]) {
cout << 0;
exit(0);
}
f[1][n] = 1;
for (int posle = 2, x = (diagonal - 1) / 2 + 1; posle <= x; posle++) {
for (auto i : list_i[posle]) {
int posri = n + m + 2 - posle;
for (auto u : list_i[posri]) {
if (f[i][u] == 0) continue;
process(i, posle - i, u, posri - u);
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) f[i][j] = g[i][j], g[i][j] = 0;
}
if (diagonal & 1) {
for (int i = 1; i <= n; i++) add(res, f[i][i]);
}
else {
for (int i = 1; i <= n; i++) add(res, f[i][i]), add(res, f[i][i + 1]);
}
cout << res;
}
```
### Code Hoa
```cpp=
#include <bits/stdc++.h>
#define name "cownav"
#define fi(i,a,b) for(int i = a; i <= b; i++)
#define fid(i,a,b) for(int i = a; i >= b; i--)
#define ll long long
#define f first
#define se second
#define pii pair<int, ll>
#define getbit(i, j) ((i >> j) & 1)
#define pb push_back
#define all(v) v.begin(), v.end()
#define maxn 505
const int M = 1e9 + 7;
using namespace std;
mt19937_64 rng(time(0));
int n, m;
ll dp[maxn][maxn];
char s[maxn][maxn];
void palin() {
int len = (m + n) / 2 - 1;
fi(a, 1, len) {
fid(i, m, 1) {
int j = a - i + 2;
if (j < 1 || j > n) continue;
fi(u, 1, m) {
int v = m + n - u - a;
if (v < 1 || v > n) continue;
if (s[i][j] != s[u][v]) { dp[i][u] = 0; continue; }
dp[i][u] = (dp[i][u] + dp[i - 1][u] + dp[i][u + 1] + dp[i - 1][u + 1]) % M;
}
}
}
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(NULL);
cin >> m >> n;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++) cin >> s[i][j];
if (s[1][1] != s[m][n]) { cout << 0; return 0; }
dp[1][m] = 1;
palin();
ll res = 0;
fi(i, 1, m) res = (res + dp[i][i]) % M;
if ((m + n) % 2) fi(i, 1, m - 1) res = (res + dp[i][i + 1]) % M;
cout << res;
return 0;
}
```
## B. Khó Hiểu Xâu
Gọi hai xâu cần so sánh là $S$ và $T$ có độ dài $len$.
Gọi $S_a$ là xâu mà $S_a[i]$ = $1$ nếu $S_i$ = $a$, ngược lại $S_a[i]$ = $0$.
Giả sử $f(a) = c$ (mỗi $a$ ở $S$ thì bằng $c$ ở $T$, và mỗi $c$ ở $T$ thì bằng $a$ ở $S$) thì ta thấy $S_a$ = $T_c$.
Do đó $26$ xâu ở $S$ hợp với $26$ xâu ở $T$ phải tạo thành các cặp xâu bằng nhau, sao cho không xâu nào không được ghép cặp, và mỗi xâu chỉ thuộc duy nhất một cặp. ($A$)
Để so sánh hai xâu bằng nhau nhanh, thì ta dùng HASH. Để kiểm tra điều kiện ($A$) ta đơn giản là sort hai dãy lại, rồi kiểm tra xem hai dãy có giống nhau không.
```cpp=
#include <bits/stdc++.h>
#define VanLoi ""
#define gb(i, j) ((i >> j) & 1)
using namespace std;
const int N = (int)2e5 + 5, MOD = (int)1e9 + 7, mod = (int)1e9 + 20041203;
const int base = 31, BASE = 37;
int n, m, h[N][30], H[N][30], pw[N], PW[N];
string s;
struct pii {
int F, S;
bool operator < (const pii& x) const {
if (F == x.F) return S < x.S;
return F < x.F;
}
};
pii get(int l, int r, int state) {
int x = (1ll * h[r][state] - 1ll * h[l - 1][state] * pw[r - l + 1] + 1ll * mod * mod) % mod;
int y = (1ll * H[r][state] - 1ll * H[l - 1][state] * PW[r - l + 1] + 1ll * MOD * MOD) % MOD;
return { x, y };
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n >> m;
cin >> s;
s = ' ' + s;
pw[0] = PW[0] = 1;
for (int i = 1; i <= n; i++) {
pw[i] = 1ll * pw[i - 1] * base % mod;
PW[i] = 1ll * PW[i - 1] * BASE % MOD;
for (int j = 0; j <= 25; j++) {
int val = 0;
if (s[i] - 'a' == j) val = 1;
h[i][j] = (1ll * h[i - 1][j] * base + val) % mod;
H[i][j] = (1ll * H[i - 1][j] * BASE + val) % MOD;
}
}
while (m--) {
int x, y, len;
cin >> x >> y >> len;
vector <pii> vec_x, vec_y;
for (int i = 0; i <= 25; i++) {
vec_x.push_back(get(x, x + len - 1, i));
vec_y.push_back(get(y, y + len - 1, i));
}
sort(vec_x.begin(), vec_x.end());
sort(vec_y.begin(), vec_y.end());
bool ok = true;
if (vec_x.size() != vec_y.size()) ok = false; else {
for (int i = 0; i < (int)vec_x.size(); i++) if (vec_x[i].F != vec_y[i].F || vec_x[i].S != vec_y[i].S) {
ok = false;
break;
}
}
if (ok) cout << "YES\n"; else cout << "NO\n";
}
}
```
## C. Dãy số của Trung
$f(l, r)$ = $\sum\limits_{i=1}^{r - l + 1} a_i \cdot i$ = $\sum\limits_{i=1}^{r - l + 1} a_i$ $\cdot$ (số thằng < $a_i$ + $1$). ($A$)
Với $i$, giá trị $a_i$, xét một thằng nhỏ hơn nó bên trái: $j < i$ và $a_j < a_i$, để thằng $j$ donate vào đáp án thì $j$ và $i$ phải cùng thuộc một $l, r$.
=> $l$ sẽ giao động từ $1$ đến $j$, $r$ sẽ giao động từ $i$ đến $n$.
=> số cách là $j \cdot (n - i + 1)$.
Với mỗi cách này, dựa vào công thức ($A$) ta thấy đáp án sẽ tăng lên $a_i \cdot 1$ = $a_i$.
Vậy $ans$ += $a_i \cdot j \cdot (n - i + 1)$ .
Ta thấy $a_i \cdot (n - i + 1)$ cố định, còn $j$ ta có thể dùng IT hoặc BIT để tính.
Tương tự với một thằng nhỏ hơn $a_i$ bên phải $i$.
```cpp=
#include <bits/stdc++.h>
#define VanLoi ""
#define gb(i, j) ((i >> j) & 1)
using namespace std;
const int N = (int)5e5 + 5, MOD = (int)1e9 + 7, mod = (int)1e9 + 20041203;
int n, res, sum[N * 4][2];
void add(int& x, int y) {
x += y;
if (x >= MOD) x -= MOD;
}
struct pii {
int F, S;
bool operator < (const pii& x) const {
return F < x.F;
}
} a[N];
void ud(int id, int l, int r, int pos) {
add(sum[id][0], pos);
add(sum[id][1], n - pos + 1);
if (l == r) return;
int m = (l + r) >> 1;
if (pos <= m) ud(id << 1, l, m, pos);
else ud(id << 1 | 1, m + 1, r, pos);
}
int get(int id, int l, int r, int d, int c, int state) {
if (r < d || l > c) return 0;
if (d <= l && r <= c) return sum[id][state];
int m = (l + r) >> 1;
int x = get(id << 1, l, m, d, c, state);
add(x, get(id << 1 | 1, m + 1, r, d, c, state));
return x;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i].F, a[i].S = i;
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) {
/// trái
int ans = 1ll * get(1, 1, n, 1, a[i].S, 0) * (n - a[i].S + 1) % MOD;
/// phải
ans = (1ll * get(1, 1, n, a[i].S, n, 1) * a[i].S + ans) % MOD;
/// chính nó
ans = (1ll * a[i].S * (n - a[i].S + 1) + ans) % MOD;
res = (1ll * ans * a[i].F + res) % MOD;
ud(1, 1, n, a[i].S);
}
cout << res;
}
```