A
只需要预处理前后缀 ,然后枚举关键点
,用前后缀的
再求一次
即可求出剩余关键点的
,然后比较大小贡献答案。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9;
int n;
int read() {
int f = 0, x = 0;
char ch = getchar();
while (!isdigit(ch)) {
f |= (ch == '-');
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return f ? -x : x;
}
struct Tree {
int fa[N][20], dep[N], a[N];
vector<int>e[N];
void dfs(int x, int f) {
fa[x][0] = f, dep[x] = dep[f] + 1;
for (int i = 1; i <= 17; i++)
fa[x][i] = fa[fa[x][i - 1]][i - 1];
for (int to : e[x])
if (to != f)
dfs(to, x);
}
int LCA(int x, int y) {
if (x == 0 || y == 0)
return x + y;
if (dep[x] < dep[y])
swap(x, y);
for (int i = 17; i >= 0; i--)
if (dep[x] - (1 << i) >= dep[y])
x = fa[x][i];
if (x == y)
return x;
for (int i = 17; i >= 0; i--)
if (fa[x][i]^fa[y][i])
x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
void init() {
for (int i = 1; i <= n; i++)
a[i] = read();
for (int i = 2; i <= n; i++) {
int x = read();
e[i].push_back(x);
e[x].push_back(i);
}
dfs(1, 0);
}
} T1, T2;
int a[N], L[N][2], R[N][2], k;
int main() {
n = read(), k = read();
for (int i = 1; i <= k; i++)
a[i] = read();
T1.init(), T2.init();
for (int i = 1; i <= k; i++) {
L[i][0] = T1.LCA(L[i - 1][0], a[i]);
L[i][1] = T2.LCA(L[i - 1][1], a[i]);
}
for (int i = k; i; i--) {
R[i][0] = T1.LCA(R[i + 1][0], a[i]);
R[i][1] = T2.LCA(R[i + 1][1], a[i]);
}
int ans = 0;
for (int i = 1; i <= k; i++) {
int x0 = T1.LCA(L[i - 1][0], R[i + 1][0]);
int x1 = T2.LCA(L[i - 1][1], R[i + 1][1]);
if (T1.a[x0] > T2.a[x1])
ans++;
}
cout << ans << endl;
return 0;
}B
首先有一个做法就是,建图然后跑最小费用最大流。显然过不了。
模拟费用流的过程,每次都是把一些人从某个城市移动到另一个城市。
一个人 若从城市
移动到城市
,代价就是
。因为增广路只会经过每个点至多一次,所以如果有多个人可以从城市
移动到城市
,肯定会优先考虑代价最小的人。
那么不妨只建立 个点,城市
到
之间的边就是所有在城市
的人移动到城市
的代价。 显然边的数量还是
级别,但是对于重边只取代价最小的边,所以开
个小根堆来维护所有的边。这样就可以放心跑
了!但是注意每跑一次就要重新更新边集,初始把所有人放在城市
。
因为会跑 次
,并且每次会移动至多
个人所在城市,所以时间复杂度最坏是
( 最坏是
)
#include <bits/stdc++.h>
#define int long long
#define w first
#define se second
#define mk make_pair
using namespace std;
const int inf = 1e9;
const int N = 1e5 + 9;
int read() {
int f = 0, x = 0;
char ch = getchar();
while (!isdigit(ch)) {
f |= (ch == '-');
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return f ? -x : x;
}
int n, k;
int dis[12], vis[12], fa[12];
int a[12], c[N][12], in[N];
priority_queue<pair<int, int>> e[12][12];
int SPFA(int rt) {
queue<int> q;
q.push(rt);
for (int i = 1; i <= 10; i++)
dis[i] = inf, vis[i] = 0, fa[i] = 0;
dis[rt] = 0, vis[rt] = 1;
while (q.size()) {
int x = q.front();
q.pop();
vis[x] = 0;
for (int y = 1; y < rt; y++) {
if (!e[x][y].size())
continue;
int d = -e[x][y].top().w;
if (dis[y] > dis[x] + d) {
dis[y] = dis[x] + d;
fa[y] = x;
if (vis[y])
continue;
vis[y] = 1;
q.push(y);
}
}
}
vector<int>A;
int x = 1;
while (x != rt) {
A.push_back(e[fa[x]][x].top().se);
in[e[fa[x]][x].top().se] = fa[x];
x = fa[x];
}
for (auto x : A)
for (int i = 1; i <= rt; i++)
if (i != x)
e[i][in[x]].push(mk(-c[x][i] + c[x][in[x]], x));
return dis[1];
}
signed main() {
n = read(), k = read();
for (int i = 1; i <= k; i++)
a[i] = read();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= k; ++j)
c[i][j] = read();
int ans = 0;
for (int i = 1; i <= n; i++)
ans += c[i][1], in[i] = 1;
for (int j = 2; j <= k; ++j) {
for (int i = 1; i <= n; i++)
e[j][in[i]].push(mk(c[i][in[i]] - c[i][j], i));
int k = a[j];
while (k--) {
for (int u = 1; u <= j; ++u)
for (int v = 1; v <= j; ++v)
while (e[u][v].size() && in[e[u][v].top().se] != v)
e[u][v].pop();
ans += SPFA(j);
}
}
printf("%lld\n", ans);
return 0;
}C
若字符串 在字符串
前面,那么需要满足
。直接 sort 就可以做到
的复杂度。
不妨将 和
画出来观察(这里假设
)
aaabbbbbb bbbbbbaaa
这里用 a 来代替 中字符,
b 来代替 中字符。同时用
表示字符串
以
开头的后缀。
我们发现,若 不是
的前缀,我们可以直接比较
的大小。这里可以用
比较
序来实现,
否则,把 分成三部分,其中第一部分为开头长为
的字符串,第三部分为末尾长为
的字符串,剩下字符为第二部分。那么就可以依次比较一、二、三部分的字符串从而得到
和
的大小关系。
由于 是
的前缀,所以第一部分是相同的。而第二部分就是
与
本身比较大小。这使我们想到 exkmp ,因为 exkmp 能在线性时间内求出字符串
的每一个后缀与
的
。
若第二部分也相同,我们比较第三部分,也就是 与
比较大小。由于
是
的前缀,所以等价于
和
比较大小。也能用 exkmp 快速找到
并比较下一位字符。
至此,时间复杂度 ,但由于
常数较大,需要进行一些写法上的优化。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 9;
void chmx(int &x, int y) {
(x < y) &&(x = y);
}
void chmn(int &x, int y) {
(x > y) &&(x = y);
}
int read() {
int f = 0, x = 0;
char ch = getchar();
while (!isdigit(ch)) {
f |= (ch == '-');
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return f ? -x : x;
}
vector<int>z[N];
string s[N];
void get(vector<int> &z, string &s) {
int n = s.size() - 1;
z.resize(n + 1);
z[1] = n;
for (int i = 2, l = 0, r = 0; i <= n; i++) {
if (i <= r)
z[i] = min(z[i - l + 1], r - i + 1);
while (i + z[i] <= n && s[i + z[i]] == s[z[i] + 1])
++z[i];
if (i + z[i] - 1 > r)
l = i, r = i + z[i] - 1;
}
}
int ed[N], dfn[N * 10], sz[N * 10], b[N];
struct trie {
int son[N * 10][5];
int idx, id;
trie() {
idx = 1;
}
int add(string &s) {
int x = 1, n = s.size() - 1;
for(int i = 1;i <= n; i++) {
int nt = s[i] - '0';
if (!son[x][nt])
son[x][nt] = ++idx;
x = son[x][nt];
}
return x;
}
void dfs(int x) {
dfn[x] = ++id;
sz[x] = 1;
for(int i = 0;i <= 4; i++)if (son[x][i]) {
dfs(son[x][i]);
sz[x] += sz[son[x][i]];
}
}
} T;
int a[N], len[N];
bool in(int x, int y) {
x = ed[x];
y = ed[y];
return dfn[x] <= dfn[y] && dfn[y] < dfn[x] + sz[x] ||
dfn[y] <= dfn[x] && dfn[x] < dfn[y] + sz[y];
}
bool cmp(int x, int y) {
if (!in(x, y))
return dfn[ed[x]] < dfn[ed[y]];
if (ed[x] == ed[y])
return 0;
if (len[x] < len[y]) {
int k = z[y][len[x] + 1];
if (k == len[y] - len[x]) {
k = z[y][len[y] - len[x] + 1];
if (k == len[x])
return 0;
return s[y][len[y] - len[x] + k + 1] < s[x][k + 1];
}
return s[y][k + 1] < s[y][len[x] + k + 1];
} else {
swap(x, y);
int k = z[y][len[x] + 1];
if (k == len[y] - len[x]) {
k = z[y][len[y] - len[x] + 1];
if (k == len[x])
return 0;
return s[y][len[y] - len[x] + k + 1] > s[x][k + 1];
}
return s[y][k + 1] > s[y][len[x] + k + 1];
}
}
signed main() {
int n = read();
for(int i = 1;i <= n; i++) {
cin >> s[i];
s[i] = "0" + s[i];
len[i] = s[i].size() - 1;
get(z[i], s[i]);
ed[i] = T.add(s[i]);
a[i] = i;
}
T.dfs(1);
sort(a + 1, a + 1 + n, cmp);
for(int i = 1;i <= n; i++) for(int j = 1;j <= len[a[i]]; j++)putchar(s[a[i]][j]);
return 0;
}
D
对于 的情况,设
表示从
走到
的期望步数。最终答案就是
到
路径上每条边的期望步数之和。那么有转移式子:
把 移到左边去
可知, 最终等于
。因为子树内每一条边会被计算两次,
到
的边会被计算一次。
考虑一条单向边 的贡献,对于子树内的
没有影响。而对于
,相比原来的值减少了
,也就是减少了
,并且对于所有是
祖先的
值都减少了
。因为这等价于直接砍掉
这棵子树(因为到不了)。
换言之,一个点 对祖先
有贡献当且仅当它们之间不存在单向边。这个贡献可以组合数来求。并且由于产生贡献的距离连续,在 dfs 的时候维护区间左右端点然后区间求和。
具体的,对于距离为 的两个点
,
对于
的贡献为:
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int N = 1e6 + 9;
const int p = 998244353;
bool vis[N];
int n, k, s, fa[N], dep[N], cnt, D;
int a[N], ans;
vector <int> e[N];
int read() {
int f = 0, x = 0;
char ch = getchar();
while (!isdigit(ch)) {
f |= (ch == '-');
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return f ? -x : x;
}
int inv(int x, int base = p - 2) {
int res = 1;
while (base) {
if (base & 1)
res = 1ll * res * x % p;
x = 1ll * x * x % p;
base >>= 1;
}
return res;
}
void dfs(int x, int f) {
fa[x] = f, dep[x] = dep[f] + 1;
for (auto v : e[x])
if (v != f)
dfs(v, x);
}
int calc(int x, int y) {
return y ? (a[x + y - 1] + p - a[y - 1]) % p : a[x + y - 1];
}
void dfs(int x) {
if (dep[x])
ans = (ans + calc(cnt, D)) % p;
for (auto to : e[x])
if (to != fa[x]) {
cnt += vis[to];
D += 1 - vis[to];
dfs(to);
cnt -= vis[to];
D -= 1 - vis[to];
}
}
signed main() {
n = read(), k = read(), s = read();
for (int i = 1; i <= n - 1; i++) {
int x = read(), y = read();
e[x].pb(y);
e[y].pb(x);
}
dep[0] = -1;dfs(1, 0);
for (int x = s; x; x = fa[x])
vis[x] = 1;
a[0] = 1;
for (int i = 1; i <= n; i++)
a[i] = a[i - 1] * (n - i - k) % p * inv(n - i, p - 2) % p;
for (int i = 1; i <= n; i++)
a[i] = (a[i] + a[i - 1]) % p;
dfs(1);
ans = (ans * 2 - dep[s] + p) % p;
printf("%lld\n", ans);
return 0;
}E
如果两个电线相交,我们交换第一个相遇的位置以后的路径,这样就从原本的 变成了
,也就是交换终点。因为不合法的方案与
两两对应,所以最后答案就是
的方案数减去
的方案数。
考虑如何计算从 到
的方案数(其余同理)。对于一条弦
,设交点编号按照从
到
的顺序分别为
。 发现贡献这些交点的弦的起点也是从小到大的顺序。并且对于一条从
或
进入的路径,可以从任意
或
出去。那么记录
表示终点在第
条最后一个交点的方案数。
表示终点在圆上第
个顶点的方案数。对于转移,有如下几种:
- 当前点由上一个点走一条圆弧得来,
。
- 当前点是一条弦
的终点,
。
- 以当前起点新增一条弦
,按交点顺序遍历弦
。显然
就是所有
的和加上
(
是这条弦的起点)。由于弦
和弦
的交点肯定是弦
的最后一个交点,所以对所有的
做一遍前缀和再加上
。
时间复杂度 。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 9;
const int p = 998244353;
int n, m, L, res1, res2;
int a[N], id[N], f[N], g[N];
int read() {
int f = 0, x = 0;
char ch = getchar();
while (!isdigit(ch)) {
f |= (ch == '-');
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return f ? -x : x;
}
void work(int S) {
memset(g, 0, sizeof g);
memset(f, 0, sizeof f);
f[S] = 1;
for (int i = S, j = 1; i <= n; i++) {
if(i != S)f[i] = f[i - 1];
if (i == a[j] + L)
(f[i] += g[j]) %= p, j++;
if (!id[i])
continue;
int sum = f[i];
for (int k = j; k <= id[i]; k++)
(g[k] += sum) %= p, sum = g[k];
}
}
signed main() {
n = read(), m = read(), L = read();
for (int i = 1; i <= m; i++)
a[i] = read();
sort(a + 1, a + 1 + m);
for (int i = 1; i <= m; i++)
id[a[i]] = i;
work(1);
res1 = f[n] + 1, res2 = f[n - 1];
work(2);
res1 = (1ll * res1 * f[n - 1] + p - 1ll * res2 * f[n] % p ) % p;
cout << res1 << endl;
return 0;
}F
对于树上的问题,发现有解当且仅当图是一条链,且询问的两点是链的端点。
对于一般无向图,有解当且仅当建出圆方树后当且仅当方点构成一条链,询问两点分别位于两个方点端点,并且两个点不能是割点。
用 求解点双,时间复杂度
。
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N = 4e5 + 9;
int n, m, q, idx, top;
int dfn[N], sta[N], low[N], color, d[N], w[N];
vector <int> c[N], e[N], E[N];
void chmn(int &x, int y) {
(x > y) &&(x = y);
}
int read() {
int f = 0, x = 0;
char ch = getchar();
while (!isdigit(ch)) {
f |= (ch == '-');
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return f ? -x : x;
}
void tarjan(int x) {
dfn[x] = low[x] = ++ idx;
sta[++ top] = x;
for (auto to : e[x]) {
if (!dfn[to]) {
tarjan(to), chmn(low[x], low[to]);
if (low[to] >= dfn[x]) {
c[++ color].push_back(x);
int now = 0;
while (now != to)
now = sta[top --], c[color].pb(now);
}
} else
low[x] = min(low[x], dfn[to]);
}
}
bool flag = 1;
int main() {
n = read(), m = read();
for (int i = 1; i <= m; i++) {
int x = read(), y = read();
e[x].pb(y), e[y].pb(x);
}
tarjan(1);
for (int i = 1; i <= n; i++)
if (!dfn[i]) {
flag = 0;
break;
}
int L = 0, R = 0;
if (flag) {
for (int i = 1; i <= color; i++)
for (auto x : c[i])
E[n + i].pb(x), E[x].pb(n + i), ++ d[x], ++ d[n + i];
for (int x = 1; x <= n + color; x++)
if (d[x] > 1)
for (auto to : E[x])
if (d[to] > 1)
++ w[x];
for (int i = 1; i <= n + color; i++)
if (d[i] > 1) {
if (w[i] > 2) {
flag = 0;
break;
}
if (!w[i]) {
L = R = i;
break;
}
if (w[i] == 1) {
L = R;
R = i;
}
}
}
q = read();
while (q --) {
int x = read(), y = read();
if (!flag) {
puts("NO");
continue;
}
if (E[x].size() != 1 || E[y].size() != 1) {
puts("NO");
continue;
}
int fx = E[x][0], fy = E[y][0];
if (fx > fy)
swap(fx, fy);
puts((fx == L && fy == R) ? "YES" : "NO");
}
return 0;
}G
若把一个凸包看成相对于另一个凸包在移动,就转化为只有一个凸包在移动的问题了!
首先他们相撞时,一定是其中一个凸包的顶点与另一个凸包有交,这启发我们枚举相交顶点。
问题变成了一个凸包移动多久后会与一个点 相交。求法就是做一条平行于凸包速度,过
的直线
,标记
和凸包的交点为
,答案就是
之间的距离除以速度。
但因为这条直线和凸包可能会有两个交点,所以需要按照速度的法向量将凸包切成两半。而对于某一半凸包和一个点 的答案,只需要二分求出交点在凸包哪条线段上即可。
还有一种方法就是二分时间求出移动距离。做闵可夫斯基和求出凸包扫过的区域,然后判断两个凸包是否有交。但是常数略大。
#include <bits/stdc++.h>
#define y1 regrtefbgth
using namespace std;
using point = complex<double>;
#define x real
#define y imag
struct line {
point a;
point b;
line(const point &a, const point &b) : a(a), b(b) {}
};
int sgn(const point &a) {
if (a.y() > 0 || (a.y() == 0 && a.x() > 0)) {
return 1;
} else {
return 0;
}
}
double dot(const point &a, const point &b) {
return (conj(a) * b).x();
}
double cross(const point &a, const point &b) {
return (conj(a) * b).y();
}
point intersection(const line &l1, const line &l2) {
return l1.a + (l1.b - l1.a) * (cross(l2.b - l2.a, l1.a - l2.a) / cross(l2.b - l2.a, l1.a - l1.b));
}
int n, m;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout << fixed << setprecision(10);
cin >> n;
vector<point> a(n);
for (int i = 0; i < n; i++) {
int x, y;
cin >> x >> y;
a[i] = point(x, y);
}
cin >> m;
vector<point> b(m);
for (int i = 0; i < m; i++) {
int x, y;
cin >> x >> y;
b[i] = point(-x, -y);
}
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
point v(x2 - x1, y2 - y1);
point sa = a[0], sb = b[0];
for (int i = 0; i < n; i++) {
if (a[i].y() < sa.y() || (a[i].y() == sa.y() && a[i].x() < sa.x())) {
sa = a[i];
}
}
for (int i = 0; i < m; i++) {
if (b[i].y() < sb.y() || (b[i].y() == sb.y() && b[i].x() < sb.x())) {
sb = b[i];
}
}
auto s = sa + sb;
vector<point> d(n + m);
for (int i = 0; i < n; i++) {
d[i] = a[(i + 1) % n] - a[i];
}
for (int i = 0; i < m; i++) {
d[n + i] = b[(i + 1) % m] - b[i];
}
sort(d.begin(), d.end(), [&](point a, point b) {
if (sgn(a) != sgn(b)) {
return sgn(a) == 1;
}
return cross(a, b) > 0;
});
vector<point> c(n + m);
c[0] = s;
for (int i = 0; i < n + m - 1; i++) {
c[i + 1] = c[i] + d[i];
}
int N = n + m;
bool in = true;
for (int i = 0; i < N; i++) {
if (cross(c[i], c[(i + 1) % N]) < 0) {
in = false;
}
}
if (in) {
puts("0");
return 0;
}
if (v == point(0, 0)) {
puts("-1");
return 0;
}
double ans = -1;
for (int i = 0; i < N; i++) {
auto a = c[i], b = c[(i + 1) % N];
if (cross(b - a, v) == 0) {
continue;
}
auto p = intersection(line(0, v), line(a, b));
if (dot(v, p) >= 0 && dot(p - a, b - a) >= 0 && dot(p - b, a - b) >= 0) {
double t = sqrt(dot(p, p) / dot(v, v));
if (ans < 0 || ans > t) {
ans = t;
}
}
}
if (ans < 0) {
puts("-1");
return 0;
}
cout << ans << endl;
return 0;
}H
前置知识:后缀数组
先将所有串连到一起,中间加分隔符,然后后缀排序,并求出 数组。
对于 中以第
个字符为开头的后缀,设它的
为
,求出
串中
比
小的最大的
值和
比
大的最小的
值,分别设为
和
,那么该后缀所能贡献的最大的答案(不妨设为
)即为
。其中
表示
的最大子段和,
求法可以使用线段树维护,具体方法可见 GSS1 的题解。
剩余的问题是如何找到 的前驱后继(即
和
),只需要枚举
,用变量记录最后一次出现的
中位置,正反各扫一遍即可,求
区间
也不需要
表,在遇到
中位置时改为该位置
,否则取
即可。
时间复杂度 。
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxm = 1e5 + 10, maxn = 1.2e6 + 10;
std::vector<int> sa_is(const std::vector<int> &s, int upper) {
int n = int(s.size());
std::vector<int> sa(n);
std::vector<bool> ls(n);
for (int i = n - 2; i >= 0; i--) {
ls[i] = (s[i] == s[i + 1]) ? ls[i + 1] : (s[i] < s[i + 1]);
}
std::vector<int> sum_l(upper + 1), sum_s(upper + 1);
for (int i = 0; i < n; i++) {
if (!ls[i]) {
sum_s[s[i]]++;
} else {
sum_l[s[i] + 1]++;
}
}
for (int i = 0; i <= upper; i++) {
sum_s[i] += sum_l[i];
if (i < upper)
sum_l[i + 1] += sum_s[i];
}
auto induce = [&](const std::vector<int> &lms) {
std::fill(sa.begin(), sa.end(), -1);
std::vector<int> buf(upper + 1);
std::copy(sum_s.begin(), sum_s.end(), buf.begin());
for (auto d : lms) {
if (d == n)
continue;
sa[buf[s[d]]++] = d;
}
std::copy(sum_l.begin(), sum_l.end(), buf.begin());
sa[buf[s[n - 1]]++] = n - 1;
for (int i = 0; i < n; i++) {
int v = sa[i];
if (v >= 1 && !ls[v - 1]) {
sa[buf[s[v - 1]]++] = v - 1;
}
}
std::copy(sum_l.begin(), sum_l.end(), buf.begin());
for (int i = n - 1; i >= 0; i--) {
int v = sa[i];
if (v >= 1 && ls[v - 1]) {
sa[--buf[s[v - 1] + 1]] = v - 1;
}
}
};
std::vector<int> lms_map(n + 1, -1);
int m = 0;
for (int i = 1; i < n; i++) {
if (!ls[i - 1] && ls[i]) {
lms_map[i] = m++;
}
}
std::vector<int> lms;
lms.reserve(m);
for (int i = 1; i < n; i++) {
if (!ls[i - 1] && ls[i]) {
lms.push_back(i);
}
}
induce(lms);
if (m) {
std::vector<int> sorted_lms;
sorted_lms.reserve(m);
for (int v : sa) {
if (lms_map[v] != -1)
sorted_lms.push_back(v);
}
std::vector<int> rec_s(m);
int rec_upper = 0;
rec_s[lms_map[sorted_lms[0]]] = 0;
for (int i = 1; i < m; i++) {
int l = sorted_lms[i - 1], r = sorted_lms[i];
int end_l = (lms_map[l] + 1 < m) ? lms[lms_map[l] + 1] : n;
int end_r = (lms_map[r] + 1 < m) ? lms[lms_map[r] + 1] : n;
bool same = true;
if (end_l - l != end_r - r) {
same = false;
} else {
while (l < end_l) {
if (s[l] != s[r]) {
break;
}
l++;
r++;
}
if (l == n || s[l] != s[r])
same = false;
}
if (!same)
rec_upper++;
rec_s[lms_map[sorted_lms[i]]] = rec_upper;
}
auto rec_sa =
sa_is(rec_s, rec_upper);
for (int i = 0; i < m; i++) {
sorted_lms[i] = lms[rec_sa[i]];
}
induce(sorted_lms);
}
return sa;
}
int id[maxn], inf = 255, len, mx, n, pos[maxm], rk[maxn];
vector<int>s, sa;
char t[maxm];
inline void suffix_sort() {
sa = sa_is(s, inf);
for (int &i : sa)
++i;
sa.insert(sa.begin(), 0);
for (int i = 0; i < sa.size(); ++i)
rk[sa[i]] = i;
}
int ht[maxn];
inline void calc_ht() {
for (int i = 1, h = 0; i <= n; ++i) {
if (h)
--h;
int j = sa[rk[i] - 1];
while (s[i - 1 + h] == s[j - 1 + h])
++h;
ht[rk[i]] = h;
}
}
int a[maxm], k, m;
struct node {
ll l, mx, r, sum;
inline node(ll v = 0): mx(v) {}
inline node(ll l_, ll mx_, ll r_, ll sum_): l(l_), mx(mx_), r(r_), sum(sum_) {}
inline node operator+(const node &k)const {
if (mx == LLONG_MIN)
return k;
return {max(l, sum + k.l), max({mx, r + k.l, k.mx}), max(r + k.sum, k.r), sum + k.sum};
}
};
struct SGT {
node v[maxm << 2];
void build(int p, int l, int r) {
if (l == r) {
v[p] = {a[l], a[l], a[l], a[l]};
return;
}
int mid = l + r >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
v[p] = v[p << 1] + v[p << 1 | 1];
}
node query(int p, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr)
return v[p];
int mid = l + r >> 1;
node ret(LLONG_MIN);
if (ql <= mid)
ret = ret + query(p << 1, l, mid, ql, qr);
if (qr > mid)
ret = ret + query(p << 1 | 1, mid + 1, r, ql, qr);
return ret;
}
} T;
ll ans[maxm];
int main() {
scanf("%d%d%d%s", &n, &m, &k, t + 1);
len = n;
for (int i = 1; i <= len; ++i)
s.push_back(t[i]);
for (int i = 1; i <= m; ++i)
scanf("%d", a + i);
T.build(1, 1, m);
for (int i = 1; i <= k; ++i) {
scanf("%s", t + 1);
pos[i] = ++n, s.push_back(++inf);
for (int j = 1; j <= m; ++j)
id[++n] = i, s.push_back(t[j]);
}
suffix_sort();
calc_ht();
for (int i = 1, h = 0; i <= n; ++i) {
if (1 <= sa[i] && sa[i] <= len)
h = ht[i + 1];
else if (h && id[sa[i]])
ans[id[sa[i]]] = max(ans[id[sa[i]]], T.query(1, 1, m, sa[i] - pos[id[sa[i]]],
sa[i] - pos[id[sa[i]]] + h - 1).mx);
h = min(h, ht[i + 1]);
}
for (int i = n, h = 0; i; --i) {
if (1 <= sa[i] && sa[i] <= len)
h = ht[i];
else if (h && id[sa[i]])
ans[id[sa[i]]] = max(ans[id[sa[i]]], T.query(1, 1, m, sa[i] - pos[id[sa[i]]],
sa[i] - pos[id[sa[i]]] + h - 1).mx);
h = min(h, ht[i]);
}
for (int i = 1; i <= k; ++i)
printf("%lld\n", ans[i]);
return 0;
}
J
由于到达一个点时具有方向,所以我们把边看成点,在十字路口的时候连边。由于边权只有 两种,所以用
可以做到
。具体实现为,若到下一个点的边权为
,就塞入队头,否则塞入队尾。每次取队头更新。也可以用优先队列直接跑最短路。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 9;
struct node {
int x, y, w;
};
int n, sx, sy, tx, ty, ans;
int v[N][4], dis[N][4], p[5];
int read() {
int f = 0, x = 0;
char ch = getchar();
while (!isdigit(ch)) {
f |= (ch == '-');
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return f ? -x : x;
}
int main() {
deque<node>q;
n = read();
for (int i = 1; i <= n; i++)
for (int j = 0; j < 4; j++) {
v[i][j] = read();
dis[i][j] = N;
}
sx = read();
sy = read();
tx = read();
ty = read();
q.push_front(node{sx, sy, 0});
while (q.size()) {
int x, y, w, nt;
node now = q.front();
q.pop_front();
x = now.x;
y = now.y;
w = now.w;
if (!y)
continue;
for (int i = 0; i < 4; i++)
if (v[x][i] == y)
nt = i;
if (dis[x][nt] <= w)
continue;
else
dis[x][nt] = w;
for (int i = 0; i < 4; i++)
if (v[y][i] == x)
nt = (i + 1) % 4;
for(int i = 0; i < 4; i++){
if(i == nt) q.push_front(node{y, v[y][i], w});
else q.push_back(node{ y, v[y][i], w + 1});
}
}
for (int i = 0; i < 4; i++)
if (v[tx][i] == ty)
ans = dis[tx][i];
if (ans == N)
ans = -1;
cout << ans << endl;
return 0;
}

全部评论
(0) 回帖