A – 等差子序列
因为是个排列,读入时我们可以把已有的数值放在数轴上,发现只要有一个对称即可。我们可以用哈希的思想魔改一下树状数组,维护串和反串的哈希然后判断即可。
// FOJ1913.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 10100, bitnum = 133;
typedef unsigned long long ull;
int T, n, ai[MAX_N];
ull nodes[2][MAX_N], pw[MAX_N];
inline char nc()
{
static char buf[1000000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++;
}
int read()
{
int x = 0, f = 1;
char ch = nc();
while (!isdigit(ch))
ch = nc();
while (isdigit(ch))
x = (x << 3) + (x << 1) + ch - '0', ch = nc();
return x * f;
}
inline int lowbit(int x) { return x & (-x); }
void update(ull *arr, int x)
{
for (int i = x; i <= n; i += lowbit(i))
arr[i] += pw[i - x];
}
ull query(ull *arr, int x, ull ret = 0)
{
for (int i = x; i; i -= lowbit(i))
ret += arr[i] * pw[x - i];
return ret;
}
ull query(ull *arr, int l, int r) { return query(arr, r) - query(arr, l - 1) * pw[r - l + 1]; }
int main()
{
T = read();
for (int i = pw[0] = 1; i < MAX_N; i++)
pw[i] = pw[i - 1] * bitnum;
while (T--)
{
memset(nodes, 0, sizeof(nodes));
n = read();
bool flag = false;
for (int i = 1; i <= n; i++)
{
ai[i] = read();
int len = min(ai[i] - 1, n - ai[i]);
if (len && query(nodes[0], ai[i] - len, ai[i]) != query(nodes[1], n - ai[i] - len + 1, n - ai[i] + 1))
flag = true;
update(nodes[0], ai[i]), update(nodes[1], n - ai[i] + 1);
}
puts(flag ? "Y" : "N");
}
return 0;
}
B – 最短路
我们考虑一个常见套路:也就是把环缩起来变成一棵树,然后用树上差分的套路来做。当然,我们不能直接硬算,因为假设 \(LCA\) 为一个环点,那么统计环上的一段弧长就比较麻烦。我们考虑先求出从\(1\)号点的单源最段路,然后再统计每个环点的环长,可知环上两点\(A, B\)的距离为:\(\min \{ |pre[A] – pre[B]|, loop – |pre[A] – pre[B]| \} \)。其中,\(pre\)是新树上的距离,新树为原图暴力破环(且随机)后的图。
// FOJ1914.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e5 + 200;
typedef pair<int, int> pii;
typedef long long ll;
int head[MAX_N], current, n, m, q, dfn[MAX_N], low[MAX_N], stk[MAX_N], tail, ptot;
int ncnt, aff[MAX_N], up[15][MAX_N], dep[MAX_N];
ll dist[MAX_N], loops[MAX_N], prefix[MAX_N];
bool inst[MAX_N], vis[MAX_N];
vector<int> G[MAX_N];
struct edge
{
int to, nxt, weight;
} edges[MAX_N << 1];
void addpath(int src, int dst, int weight)
{
edges[current].to = dst, edges[current].nxt = head[src];
edges[current].weight = weight, head[src] = current++;
}
void shortest_path(int src)
{
memset(dist, 0x3f, sizeof(dist));
priority_queue<pii> pq;
pq.push(make_pair(0, src)), dist[src] = 0;
while (!pq.empty())
{
int u = pq.top().second;
pq.pop();
if (vis[u])
continue;
vis[u] = true;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (dist[edges[i].to] > dist[u] + edges[i].weight)
dist[edges[i].to] = dist[u] + edges[i].weight, pq.push(make_pair(-dist[edges[i].to], edges[i].to));
}
memset(vis, false, sizeof(vis));
}
void tarjan(int u, int fa)
{
dfn[u] = ++ptot;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa)
{
if (dfn[edges[i].to] == 0)
up[0][edges[i].to] = u, prefix[edges[i].to] = prefix[u] + edges[i].weight, tarjan(edges[i].to, u);
else if (dfn[edges[i].to] < dfn[u])
{
loops[++ncnt] = 0LL + prefix[u] - prefix[edges[i].to] + edges[i].weight;
for (int pt = u, pre; pt != edges[i].to;)
G[edges[i].to].push_back(pt), aff[pt] = ncnt, pre = up[0][pt], up[0][pt] = edges[i].to, pt = pre;
}
}
if (up[0][u] == fa)
G[fa].push_back(u);
}
void dfs_collect(int u, int fa)
{
dep[u] = dep[fa] + 1;
for (int i = 0, siz = G[u].size(); i < siz; i++)
if (G[u][i] != fa)
dfs_collect(G[u][i], u);
}
ll query(int x, int y)
{
if (dep[x] < dep[y])
swap(x, y);
int u = x, v = y;
for (int i = 14; i >= 0; i--)
if (dep[up[i][x]] >= dep[y])
x = up[i][x];
if (x == y)
return dist[u] - dist[v];
for (int i = 14; i >= 0; i--)
if (up[i][x] != up[i][y])
x = up[i][x], y = up[i][y];
if (aff[x] && aff[x] == aff[y])
return dist[u] - dist[x] + dist[v] - dist[y] + min(llabs(prefix[x] - prefix[y]), loops[aff[x]] - llabs(prefix[x] - prefix[y]));
return dist[u] + dist[v] - (dist[up[0][x]] << 1);
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d%d", &n, &m, &q);
for (int i = 1, u, v, w; i <= m; i++)
scanf("%d%d%d", &u, &v, &w), addpath(u, v, w), addpath(v, u, w);
shortest_path(1), tarjan(1, 0);
dfs_collect(1, 0);
for (int i = 1; i <= 14; i++)
for (int j = 1; j <= ptot; j++)
up[i][j] = up[i - 1][up[i - 1][j]];
while (q--)
{
int x, y;
scanf("%d%d", &x, &y);
printf("%lld\n", query(x, y));
}
return 0;
}
C – 排斥反应
首先这个问题可以转换成在一个表格上做互不侵犯。具体转换的方式:左上角为起点,向左走视为\(+p\),向右走视为\(+q\),因为\(\gcd(p, q) = 1\),所以表格上的数字各不相同。转换为这个问题之后,我们就可以设计一个状压 DP 的方程。发现状态必须满足两个一不相邻且前端、末端的\(1\)不相邻,所以当\(p = 10\)时,状态变少为\(123\)种。可以考虑矩阵快速幂。
// FOJ1915.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 150, mod = 19921107;
int p, q, stats[MAX_N], stat_tot;
struct matrix
{
int mat[MAX_N][MAX_N];
void clear() { memset(mat, 0, sizeof(mat)); }
int *operator[](const int &rhs) { return mat[rhs]; }
matrix operator*(const matrix &rhs)
{
matrix ret;
ret.clear();
for (int i = 1; i <= stat_tot; i++)
for (int j = 1; j <= stat_tot; j++)
for (int k = 1; k <= stat_tot; k++)
ret[i][j] = (1LL * ret[i][j] + 1LL * mat[i][k] * rhs.mat[k][j] % mod) % mod;
return ret;
}
matrix operator^(const int &rhs)
{
int tim = rhs;
matrix ret, bas = *this;
ret.clear();
for (int i = 1; i <= stat_tot; i++)
ret[i][i] = 1;
while (tim)
{
if (tim & 1)
ret = ret * bas;
bas = bas * bas;
tim >>= 1;
}
return ret;
}
} A, trans;
int main()
{
scanf("%d%d", &p, &q);
// process the stats;
for (int i = 0; i < (1 << p); i++)
{
bool flag = true;
for (int j = 1; j < p; j++)
if ((i & (1 << (j - 1))) && (i & (1 << j)))
{
flag = false;
break;
}
if (p != 1)
flag &= (!((i & 1) && (i & (1 << (p - 1)))));
if (flag)
stats[++stat_tot] = i;
}
if (q == 1)
printf("%d\n", stat_tot), exit(0);
int ans = 0;
trans.clear();
for (int i = 1; i <= stat_tot; i++)
{
A[i][i] = 1;
for (int j = 1; j <= stat_tot; j++)
if (!(stats[i] & stats[j]))
trans[i][j] = 1;
}
A = A * (trans ^ (q - 1));
for (int i = 1; i <= stat_tot; i++)
for (int j = 1; j <= stat_tot; j++)
if (!(stats[i] & stats[j]))
ans = (1LL * ans + A[i][j]) % mod;
printf("%d\n", ans);
return 0;
}