9.2训练赛
A . P1082 [NOIP2012 提高组] 同余方程
exgcd模板
#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; a++)
#define fd(a, b, c) for (int a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y) {
ll t;
return b ? t = exgcd(b, a % b, y, x), y -= a / b * x, t : (x = 1, y = 0, a);
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
ll a, b, x, y;
cin >> a >> b;
exgcd(a, b, x, y);
x = (x % b + b) % b;
cout << x;
}
B . P6216 回文匹配
manacher + kmp 复杂度\(O(n)\),计数部分要求两次前缀和,注意串长限制。kmp可以用hash代替,复杂度不变,manacher可以用hash二分代替,复杂度\(O(nlogn)\)。
#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; a++)
#define fd(a, b, c) for (int a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
typedef long long ll;
const ll N = 3e6 + 10;
ll n, m;
ll len[N * 2], f[N], ps[N];
char a[N], b[N], s[N * 2];
unsigned ans;
inline void manacher(ll n) {
for (ll i = 1, mx = 0, id; i < n; ++i) {
len[i] = mx > i ? min(len[id * 2 - i], mx - i) : 1;
for (; i >= len[i] && s[i + len[i]] == s[i - len[i]]; ++len[i])
;
if (i + len[i] > mx)
mx = i + len[i], id = i;
if (i & 1 && len[i] > m) {
ll j = i + 1 >> 1, k = len[i] - 1;
ll r = j + k / 2, rm = j + m / 2 - 1;
ll l = r - (k + 2 - m), lm = l + (r - rm);
ans += ps[r] - ps[rm] - (ps[lm] - ps[l]);
}
}
}
inline void kmp(char *p, ll m, char *t, ll n) {
for (ll i = 2, j = 0; i <= m; ++i) {
while (j && p[i] ^ p[j + 1])
j = f[j];
f[i] = p[i] ^ p[j + 1] ? j : ++j;
}
for (ll i = 1, j = 0; i <= n; ++i) {
while (j && t[i] ^ p[j + 1])
j = f[j];
j += t[i] == p[j + 1];
ps[i] = ps[i - 1] + (j == m);
}
fu(i, 1, n) ps[i] += ps[i - 1];
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m >> a + 1 >> b + 1;
kmp(b, m, a, n);
fu(i, 1, n) s[i * 2 - 1] = a[i];
manacher(n * 2);
cout << ans;
}
C . P3180 [HAOI2016]地图
这是一个仙人掌图(任意一条边至多存在于一个简单环中),考虑dfs树,对于每个环,删除其中所有边并从dfs序最小的点向其它的点连边,问题便转换成了子树查询,可以用tarjan改变dfs序转为区间查询然后莫队 + 分块维护,也可以直接线段树合并。
#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; a++)
#define fd(a, b, c) for (int a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
typedef pair<int, int> pii;
const int N = 1e5 + 1, M = 1e6 + 1, B = 320, B1 = 1e3;
int n, m, no;
int x, l, r, u, v, d, ans[N];
int w[N], W[N], sz[N], id[N];
int dfn[N], low[N], dx[N];
int cnt[M], bl[B1 + 1][2];
vector<int> g[N];
struct Q {
int l, r, y, o, i, bi;
bool operator<(const Q &a) {
return bi ^ a.bi ? bi < a.bi : (bi & 1 ? r < a.r : r > a.r);
}
} q[N];
inline void tarjan(int x, int f) {
dfn[x] = low[x] = ++d, dx[d] = x;
for (int v : g[x])
if (v ^ f) {
if (!dfn[v])
tarjan(v, x), mn(low[x], low[v]);
else
mn(low[x], dfn[v]);
}
}
void dfs(int u) {
id[u] = ++no, W[no] = w[u], ++sz[u];
for (int v : g[u])
if (!id[v] && low[v] >= dfn[u])
dfs(v), sz[u] += sz[v];
for (int v : g[u])
if (!id[v])
dfs(v), sz[dx[low[v]]] += sz[v];
}
inline void del(int x) {
int bi = W[x] / B1, f = cnt[W[x]] & 1;
if (cnt[W[x]] > 0)
--bl[bi][f];
--cnt[W[x]];
if (cnt[W[x]] > 0)
++bl[bi][!f];
}
inline void add(int x) {
int bi = W[x] / B1, f = cnt[W[x]] & 1;
if (cnt[W[x]] > 0)
--bl[bi][f];
++cnt[W[x]];
if (cnt[W[x]] > 0)
++bl[bi][!f];
}
inline int query(int y, int o) {
int r = (y + 1) / B1, res = 0;
fu(i, 0, r - 1) res += bl[i][o];
fu(i, r * B1, y) res += cnt[i] > 0 && (cnt[i] & 1) == o;
return res;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
fu(i, 1, n) cin >> w[i];
fu(i, 1, m) {
cin >> u >> v;
g[u].emplace_back(v);
g[v].emplace_back(u);
}
tarjan(1, 1);
dfs(1);
cin >> m;
fu(i, 1, m) {
cin >> q[i].o >> x >> q[i].y;
q[i].l = id[x], q[i].r = id[x] + sz[x] - 1;
q[i].i = i, q[i].bi = id[x] / B;
}
sort(q + 1, q + m + 1);
fu(i, 1, m) {
while (l < q[i].l)
del(l++);
while (l > q[i].l)
add(--l);
while (r > q[i].r)
del(r--);
while (r < q[i].r)
add(++r);
ans[q[i].i] = query(q[i].y, q[i].o);
}
fu(i, 1, m) cout << ans[i] << '\n';
}
D . P2599 [ZJOI2009]取石子游戏
设\(l[i][j]\)代表在区间\([i,j]\)左侧加上石子后达到必败态所需数量,由博弈论知识可知\(l[i][j]\)存在且唯一,\(r[i][j]\)同理,那么我们只要判断\(l[2][n] == a[1]\ ?\)。接下来进行讨论,设\(l = l[i][j - 1], r = r[i][j - 1], x = a[j]\)
- \(x = r\),令\(l[i][j] = 0\),已是必败态。
- \(x < l, x < r\),令\(l[i][j] = x\),无论先手从哪边拿石子,后手都从另一边拿相同的石子,那么必然是先手先拿完某一边的石子,后手必胜。
- \(r < x <= l\),令\(l[i][j] = x - 1\),如果先手拿左边,左边剩余石子\(y\),如果\(y >= r\),后手把右边拿成\(y + 1\),回到case3。如果\(y < r\),后手把右边拿成\(y\),回到case2。如果先手拿右边剩余石子\(y\),如果\(y = r\),后手取完左边,如果\(y < r\),后手把左边取到\(y\),回到case2,如果\(y > r\),后手把左边取到\(y - 1\),回到case3,以下分析类似。
- \(l <= x < r\),令\(l[i][j] = x + 1\)
- \(x > l, x > r\),令\(l[i][j] = x\)
#include <bits/stdc++.h>
#define fu(a, b, c) for (ll a = b; a <= c; a++)
#define fd(a, b, c) for (ll a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
typedef long long ll;
const ll N = 1e3 + 1;
ll t, n, m, no;
ll a[N], l[N][N], r[N][N];
ll funr(ll i, ll j);
ll funl(ll i, ll j) {
if (~l[i][j])
return l[i][j];
ll L = funl(i, j - 1), R = funr(i, j - 1);
if (a[j] == R)
return l[i][j] = 0;
else if (a[j] > L & a[j] > R | a[j] < L & a[j] < R)
return l[i][j] = a[j];
else
return l[i][j] = a[j] + (a[j] > R ? -1 : 1);
}
ll funr(ll i, ll j) {
if (~r[i][j])
return r[i][j];
ll L = funl(i + 1, j), R = funr(i + 1, j);
if (a[i] == L)
return r[i][j] = 0;
else if (a[i] > L & a[i] > R | a[i] < L & a[i] < R)
return r[i][j] = a[i];
else
return r[i][j] = a[i] + (a[i] > L ? -1 : 1);
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> t;
while (t--) {
cin >> n;
memset(l, -1, sizeof l), memset(r, -1, sizeof r);
fu(i, 1, n) cin >> a[i], l[i][i] = r[i][i] = a[i];
puts(funl(2, n) == a[1] ? "0" : "1");
}
}
E . P1503 鬼子进村
用set和stack维护被摧毁和修复的房子即可
#include <bits/stdc++.h>
#define fu(a, b, c) for (ll a = b; a <= c; a++)
#define fd(a, b, c) for (ll a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll N = 1e5 + 1, M = 1e2 + 1;
ll t, n, m, k;
ll a, b, c, d, e, f;
ll l, r, ans;
ll st[N];
set<ll> s;
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
s.emplace(0), s.emplace(n + 1);
char ch;
fu(i, 1, m) {
cin >> ch;
if (ch == 'R')
s.erase(st[--c]);
else {
cin >> b;
if (ch == 'D')
st[c++] = b, s.emplace(b);
else {
auto i = s.lower_bound(b);
cout << (*i == b ? 0 : *i - *--i - 1) << '\n';
}
}
}
}
F . P1058 [NOIP2008 普及组] 立体图
写一个画一个立方体的函数,从后往前,从左往右,从下往上画,记录一下边界,输出即可。
以前的代码
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int m, n, a[50][50], h, w;
char pic[401][301];
char s0[6] = "+---+", s1[7] = "| |/", s2[8] = "| | +", s3[8] = "+---+ |", s4[7] = "/ /|", s5[6] = "+---+";
void draw(int i, int j) { //以i,j为左下角画立方体
memcpy(&(pic[i][j]), s0, 5);
memcpy(&(pic[i - 1][j]), s1, 6);
memcpy(&(pic[i - 2][j]), s2, 7);
memcpy(&(pic[i - 3][j]), s3, 7);
memcpy(&(pic[i - 4][j + 1]), s4, 6);
memcpy(&(pic[i - 5][j + 2]), s5, 5);
}
int main() {
scanf("%d %d", &m, &n);
w = 4 * n + 1 + 2 * m;
for (int i = 0; i < m; i++) { //i行数
for (int j = 0; j < n; j++) { //j列数
scanf("%d", &a[i][j]);
h = max(3 * a[i][j] + 1 + 2 * (m - i), h);
}
}
memset(pic, '.', sizeof(pic));
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
for (int k = 1; k <= a[i][j]; k++) { //i,j对应画布h-2*(m-i-1),2*(m-i-1)+4*j
draw(h - 2 * (m - i - 1) - (k - 1) * 3 - 1, 2 * (m - i - 1) + 4 * j);
}
}
}
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
printf("%c", pic[i][j]);
}
printf("\n");
}
return 0;
}