"蔚来杯"2022牛客暑期多校训练营3
A.Ancestor
给定两棵有\(n\)个节点的树\(A、B\),树上节点均有一个权值,给出\(k\)个关键点的编号\(x_1,x_2,...,x_k\),问有多少种方案,使得恰好去掉一个关键点后,剩余关键点在\(A\)中LCA的权值大于\(B\)中LCA的权值?
题解做法是预处理,预处理两个关键点序列在\(A,B\)两棵树上的前后缀LCA,然后枚举去掉的关键点,用前缀后缀LCA计算出剩余节点的LCA比较权值即可
但考场上我比较头铁,写了个虚树的做法(捂脸
对\(k\)个关键点求出虚树,当我们要去掉一个节点的时候,对该节点的情况进行分类讨论,并求出新的LCA即可
/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
int f = 1; char ch = getchar();
for (; ch < '0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 1e5;
int Point[N + 10], n, K;
struct VirtualTree {
int pre[(N << 1) + 10], now[N + 10], child[(N << 1) + 10], tot;
int Fa[N + 10], Num[N + 10], Root;
void link(int x, int y) { pre[++tot] = now[x], now[x] = tot, child[tot] = y; }
void connect(int x, int y) { link(x, y), link(y, x); }
void Dfs(int x, int fa) {
Fa[x] = fa;
for (int p = now[x]; p; p = pre[p]) {
int son = child[p];
if (son == fa) continue;
Dfs(son, x), Num[x]++;
}
}
int solve(int x) {
if (x == Root) {
if (Num[x] > 1) return Root;
return child[now[Root]];
}
if (Fa[x] != Root || Num[x] || Num[Root] > 2) return Root;
for (int p = now[Root]; p; p = pre[p]) {
int son = child[p];
if (son != x) return son;
}
return 0;
}
}VTA, VTB;
struct Tree {
int pre[(N << 1) + 10], now[N + 10], child[(N << 1) + 10], tot, Time;
int W[N + 10], Deep[N + 10], Top[N + 10], Heavy[N + 10], Sz[N + 10], Fa[N + 10], Dfn[N + 10];
void link(int x, int y) { pre[++tot] = now[x], now[x] = tot, child[tot] = y; }
void connect(int x, int y) { link(x, y), link(y, x); }
void Dfs1(int x, int fa) {
Deep[x] = Deep[Fa[x] = fa] + (Sz[x] = 1);
for (int p = now[x]; p; p = pre[p]) {
int son = child[p];
if (son == fa) continue;
Dfs1(son, x), Sz[x] += Sz[son];
if (!Heavy[x] || Sz[son] > Sz[Heavy[x]])
Heavy[x] = son;
}
}
void Dfs2(int x, int fa) {
if (!x) return;
Dfn[x] = ++Time;
Top[x] = Heavy[fa] == x ? Top[fa] : x;
Dfs2(Heavy[x], x);
for (int p = now[x]; p; p = pre[p]) {
int son = child[p];
if (son == fa || son == Heavy[x]) continue;
Dfs2(son, x);
}
}
int LCA(int x, int y) {
while (Top[x] != Top[y]) {
if (Deep[Top[x]] < Deep[Top[y]]) swap(x, y);
x = Fa[Top[x]];
}
return Deep[x] < Deep[y] ? x : y;
}
void rebuild(VirtualTree& VT) {
sort(Point + 1, Point + 1 + K, [&](int x, int y) {return Dfn[x] < Dfn[y]; });
VT.Root = Point[1];
for (int i = 2; i <= K; i++) VT.Root = LCA(VT.Root, Point[i]);
vector<int>stack;
stack.push_back(VT.Root);
for (int i = 1; i <= K; i++) {
int x = Point[i], lca = LCA(x, stack.back());
if (x == VT.Root) continue;
if (lca == stack.back()) {
stack.push_back(x);
continue;
}
while (stack.size() > 1) {
int y = *prev(prev(stack.end()));
if (Dfn[y] >= Dfn[lca]) VT.connect(y, stack.back()), stack.pop_back();
else {
if (lca != stack.back()) {
VT.connect(lca, stack.back());
stack.pop_back(), stack.push_back(lca);
}
break;
}
}
stack.push_back(x);
}
while (stack.size() > 1) {
VT.connect(stack.back(), *prev(prev(stack.end())));
stack.pop_back();
}
}
void Read(VirtualTree& VT) {
for (int i = 1; i <= n; i++) W[i] = read(0);
for (int i = 2; i <= n; i++) connect(read(0), i);
Dfs1(1, 0), Dfs2(1, 0), rebuild(VT), VT.Dfs(VT.Root, 0);
}
}A, B;
void solve() {
int Cnt = 0;
for (int i = 1; i <= K; i++) {
int _A = VTA.solve(Point[i]);
int _B = VTB.solve(Point[i]);
Cnt += A.W[_A] > B.W[_B];
}
printf("%d\n", Cnt);
}
int main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = read(0), K = read(0);
for (int i = 1; i <= K; i++) Point[i] = read(0);
A.Read(VTA), B.Read(VTB);
solve();
return 0;
}
B.Boss
将\(n\)个人派遣到\(K\)个城市,每个城市需要\(e_i\)个人,将第\(i\)个人派遣至第\(j\)个城市的代价为\(c_{i,j}\),问最小代价?\((1\leqslant n\leqslant 10^5,1\leqslant K\leqslant 10)\)
一个很显然的做法是费用流,但是点数边数太多,费用流没法跑
考虑费用流的过程,每次都是把一部分人从城市\(a\)挪到城市\(b\)。如果第\(i\)个人从城市\(a\)挪到了城市\(b\),那么其代价为\(-c_{a,i}+c_{b,i}\)。考虑到增广路只会经过每个点一次,所以如果有多个人可以从城市\(a\)到城市\(b\),必然会优先考虑代价小的人
故我们考虑建\(K\)个点代表城市,\(a\)到\(b\)的边就是所有在城市\(a\)中的人到城市\(b\)的代价。显然,这样的边数依然是\(O(nK)\)级别,但两点之间会有大量的重边,而上面提到,对于重边我们仅需要考虑其最短边即可。
故我们可以开\(K^2\)个堆去维护点与点之间的重边,这样就可以跑SPFA了。
初始将所有人放在城市1,并且每跑一次就要更新一次边集
SPFA总共跑\(n\)次,每次至多移动\(K\)个人所在的城市,故时间复杂为\(O(nk^3+nk\log n)\)
/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
int f = 1; char ch = getchar();
for (; ch < '0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 1e5, K = 10;
struct node {
int x, v;
node(int _v = 0, int _x = 0) { x = _x, v = _v; }
bool operator<(const node& ots)const { return v < ots.v; }
};
struct S1 {
#define ls (p<<1)
#define rs (p<<1|1)
#define fa (p>>1)
node Q[N + 10];
size_t tot;
S1() { tot = 0; }
void push(node x) {
Q[++tot] = x;
size_t p = tot;
while (fa && Q[p] < Q[fa]) swap(Q[p], Q[fa]), p = fa;
}
node top() { return Q[1]; }
void pop() {
Q[1] = Q[tot--];
size_t p = 1, son;
while (ls <= tot) {
if (rs > tot || Q[ls] < Q[rs]) son = ls;
else son = rs;
if (Q[son] < Q[p]) swap(Q[p], Q[son]), p = son;
else break;
}
}
bool empty() { return !tot; }
#undef ls
#undef rs
#undef fa
}Edge[K + 10][K + 10];
int n, k;
int C[N + 10][K + 10], limit[K + 10], IN[N + 10];
int Dis[K + 10], Frm[K + 10];
bool Vis[K + 10];
int SPFA(int S) {
memset(Vis, 0x00, sizeof(Vis));
memset(Frm, 0x00, sizeof(Frm));
memset(Dis, 0x3f, sizeof(Dis));
queue<int>H;
H.push(S), Dis[S] = 0, Vis[S] = 1;
while (!H.empty()) {
int x = H.front();
H.pop();
for (int y = 1; y < S; y++) {
if (Edge[x][y].empty()) continue;
int V = Edge[x][y].top().v;
if (Dis[y] > Dis[x] + V) {
Dis[y] = Dis[x] + V;
Frm[y] = x;
if (!Vis[y]) {
Vis[y] = 1;
H.push(y);
}
}
}
Vis[x] = 0;
}
vector<int>temp;
int x = 1;
while (x != S) {
temp.push_back(Edge[Frm[x]][x].top().x);
IN[Edge[Frm[x]][x].top().x] = Frm[x];
x = Frm[x];
}
for (auto p : temp)
for (int i = 1; i <= S; i++)
if (i != IN[p])
Edge[i][IN[p]].push(node(C[p][i] - C[p][IN[p]], p));
return Dis[1];
}
int main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = read(0), k = read(0);
for (int i = 1; i <= k; i++) limit[i] = read(0);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= k; j++)
C[i][j] = read(0);
ll Total = 0;
for (int i = 1; i <= n; i++)
Total += C[i][IN[i] = 1];
for (int j = 2; j <= k; j++) {
for (int i = 1; i <= n; i++)
Edge[j][IN[i]].push(node(C[i][j] - C[i][IN[i]], i));
int temp = limit[j];
while (temp--) {
for (int x = 1; x <= j; x++)
for (int y = 1; y <= j; y++)
while (!Edge[x][y].empty() && IN[Edge[x][y].top().x] != y)
Edge[x][y].pop();
Total += SPFA(j);
}
}
printf("%lld\n", Total);
return 0;
}
C.Concatenation
给定\(n\)个字符串,求将它们拼接起来的字典序最小
直接排序,注意cmp判断函数里面带引用,以及先判断首字母再进行STL自带比较
/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
int f = 1; char ch = getchar();
for (; ch < '0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 2e6;
string s[N + 10];
bool cmp(const string& a,const string& b) { return a[0] != b[0] ? a[0] < b[0] : a + b < b + a; }
int main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n = read(0);
for (int i = 1; i <= n; i++) cin >> s[i];
sort(s + 1, s + 1 + n, cmp);
for (int i = 1; i <= n; i++)
printf("%s", s[i].c_str());
return 0;
}
D.Directed
给定一棵\(n\)个节点的树和一个起点\(s\),1号节点为终点,随机选择\(k\)条边变成单向边,问从起点开始,在树上随机游走到达终点的期望步数?
首先不考虑反向边,我们设\(F_u\)表示节点\(u\)随机走到其父亲的期望步数,可以得到式子:\(F_u=\frac{1}{d_u}+\sum\limits_{u\rightarrow v}\frac{1+F_v+F_u}{d_u}\),其中\(d_u\)表示点\(u\)的度数
移项可得\(F_u=d_u+\sum\limits_{u\rightarrow v}F_v\),考虑递归套入这个式子,可以发现除了其到父亲的边之外,每条边都被计算了两次,不难得出\(F_u=2\times sz_u-1\)
考虑单向边的影响,可以发现一条单向边\(<u,Fa_u>\)是不会对其子树造成任何影响的,但是它会使其所有祖先的\(F'\)减少\(2\times sz_u\)。换而言之,一个点\(y\)对祖先\(x\)有贡献当且仅当它们之间不存在单向边,如果\(x,y\)之间的距离为\(l\),则\(y\)对\(F_x\)的贡献为\(\binom{n-i-1}{k}/\binom{n-1}{k}\)
此外,注意到我们要统计的是\(s\)到1路径上所有点的\(F\)值之和,可以发现对于树上任意一个点而言,其对祖先的贡献在答案所求的路径上均为连续的一整段,故我们可以对这一整段连续的贡献进行与预处理,然后区间求和即可。
/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
int f = 1; char ch = getchar();
for (; ch < '0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 1e6, P = 998244353;
int pre[(N << 1) + 10], now[N + 10], child[(N << 1) + 10], tot;
int Deep[N + 10], F[N + 10];
void link(int x, int y) { pre[++tot] = now[x], now[x] = tot, child[tot] = y; }
void connect(int x, int y) { link(x, y), link(y, x); }
void Dfs(int x, int fa) {
Deep[x] = Deep[F[x] = fa] + 1;
for (int p = now[x]; p; p = pre[p]) {
int son = child[p];
if (son == fa) continue;
Dfs(son, x);
}
}
bool Vis[N + 10];
int Inv[N + 10], S[N + 10], Ans, L, R;
int solve(int x, int y) { return y ? (S[x + y - 1] - S[y - 1] + P) % P : S[x + y - 1]; }
void Dfs(int x) {
if (Deep[x])
Ans = (Ans + solve(L, R)) % P;
for (int p = now[x]; p; p = pre[p]) {
int son = child[p];
if (son == F[x]) continue;
L += Vis[son], R += Vis[son] ^ 1;
Dfs(son);
L -= Vis[son], R -= Vis[son] ^ 1;
}
}
int main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n = read(0), k = read(0), s = read(0);
for (int i = 1; i < n; i++) {
int x = read(0), y = read(0);
connect(x, y);
}
Deep[0] = -1;
Dfs(1, 0);
for (int x = s; x; x = F[x]) Vis[x] = 1;
Inv[0] = Inv[1] = S[0] = 1;
for (int i = 2; i <= n; i++) Inv[i] = 1ll * (P - P / i) * Inv[P % i] % P;
for (int i = 1; i <= n; i++) S[i] = 1ll * S[i - 1] * (n - i - k) % P * Inv[n - i] % P;
for (int i = 1; i <= n; i++) S[i] = (S[i] + S[i - 1]) % P;
Dfs(1);
Ans = (2ll * Ans - Deep[s] + P) % P;
printf("%d\n", Ans);
return 0;
}
E.Electrician
给定一个圆,圆上有\(n\)个等距点,和\(m\)条等长的弦。现在要在圆上铺设两条电线,一条从1到\(n\),另一条从2到\(n-1\),电线可以沿着圆环铺设,也可以沿着弦铺设,在弦与弦相交的地方也可以转到另一根弦上铺设。问有多少种方案使得电线铺设不相交?
容斥+dp,咕咕咕
F.Fief
给定一个无向图,每次询问两点\(x,y\),问是否存在一个\(n\)的排列,使得第一个元素为\(x\),最后一个元素是\(y\),且排列的任意一个前缀、后缀都在图中连通
考虑化简问题,对于树上问题而言,有解当且仅当图为一条链,且询问两点为链的端点
对于一般图而言,有解当且仅当建出圆方树后方点构成一条链,询问两点位于两个方点端点,且两个点不能是割点
/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
int f = 1; char ch = getchar();
for (; ch < '0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 1e5, M = 2e5;
int pre[(M << 1) + 10], now[N + 10], child[(M << 1) + 10], tot;
int Dfn[N + 10], Low[N + 10], stack[N + 10], Cnt[N + 10], Time, top, Number, Root;
void link(int x, int y) { pre[++tot] = now[x], now[x] = tot, child[tot] = y; }
void connect(int x, int y) { link(x, y), link(y, x); }
vector<int>bcc[N + 10];
bool Vis[N + 10];
void Tarjan(int x, int fa) {
Dfn[x] = Low[x] = ++Time;
stack[++top] = x;
if (x == Root && !now[x]) {
bcc[++Number].push_back(x);
return;
}
int cnt = 0;
for (int p = now[x]; p; p = pre[p]) {
int son = child[p];
if (!Dfn[son]) {
Tarjan(son, x);
Low[x] = min(Low[x], Low[son]);
if (Dfn[x] <= Low[son]) {
Number++, cnt++;
if (x != Root || cnt > 1) Vis[x] = 1;
bcc[Number].push_back(x);
while (stack[top] != son)
bcc[Number].push_back(stack[top--]);
bcc[Number].push_back(stack[top--]);
}
}
else Low[x] = min(Low[x], Dfn[son]);
}
}
struct Edge {
int x, y;
Edge(int _x = 0, int _y = 0) :x(_x), y(_y) {}
};
int Deg[M + 10];
int main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n = read(0), m = read(0);
vector<Edge>edges;
for (int i = 1; i <= m; i++) {
int x = read(0), y = read(0);
connect(x, y);
}
int res = 0;
for (int i = 1; i <= n; i++) {
if (!Dfn[i]) {
Root = i;
Tarjan(i, 0), res++;
}
}
int _N = Number;
for (int i = 1; i <= n; i++)
if (Vis[i])
Cnt[i] = ++_N;
for (int i = 1; i <= Number; i++) {
for (auto p : bcc[i]) {
if (Vis[p])
Deg[i]++, Deg[Cnt[p]]++;
else
Cnt[p] = i;
}
}
bool Flag = (res == 1);
for (int i = 1; i <= _N; i++)
Flag &= Deg[i] <= 2;
int q = read(0);
for (int i = 1; i <= q; i++) {
int x = Cnt[read(0)], y = Cnt[read(0)];
if (!Flag) {
printf("NO\n");
continue;
}
if (!Deg[x] || !Deg[y]) {
printf("YES\n");
continue;
}
if (Deg[x] == 1 && Deg[y] == 1 && x != y) {
printf("YES\n");
continue;
}
printf("NO\n");
}
return 0;
}
G.Geometry
给定二维平面中两个凸包,问他们相撞的时刻
计算几何难难,咕咕咕
H.Hacker
给定一个长为\(n\)的主串\(s\),再给定\(m\)个数\(V_i\),然后给定\(k\)个长为\(m\)的匹配串\(t_i\),对于每个\(t_i\),若其子串\(t_i[l...r]\)为\(s\)的子串,则记\(C_{l,r}=\sum\limits_{j=l}^rV_j\),求每个串\(t_i\)中\(C_{l,r}\)的最大值
考虑对主串建SAM,对每个模式串在SAM上进行匹配,对于\(t_i\)的位置\(j\)而言,记其匹配节点在SAM上为\(p\),则其所能匹配的最大长度为\(F_p\rightarrow len\),\(F_p\)为其在SAM上的父节点
求出最长匹配长度后,再用线段树求一次区间最大连续子段和即可
/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_set>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
int f = 1; char ch = getchar();
for (; ch < '0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 1e5;
int V[N + 10];
struct ST {
#define ls (p<<1)
#define rs (p<<1|1)
struct node {
ll l, r, now, sum;
node(ll _l = 0, ll _r = 0, ll _n = 0, ll _s = 0) { l = _l, r = _r, now = _n, sum = _s; }
void init(ll v) { l = r = now = max(0ll, sum = v); }
friend node operator+(const node& A, const node& B);
};
friend node operator +(const node& A, const node& B) {
node C;
C.l = max(A.l, A.sum + B.l);
C.r = max(A.r + B.sum, B.r);
C.now = max(max(A.now, B.now), A.r + B.l);
C.sum = A.sum + B.sum;
return C;
}
node Tree[(N << 2) + 10];
void update(int p) { Tree[p] = Tree[ls] + Tree[rs]; }
void build(int p, int l, int r) {
if (l == r) {
Tree[p].init(V[l]);
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
update(p);
}
node Query(int p, int l, int r, int L, int R) {
if (L <= l && r <= R) return Tree[p];
int mid = (l + r) >> 1;
node res = 0;
if (L <= mid) res = res + Query(ls, l, mid, L, R);
if (R > mid) res = res + Query(rs, mid + 1, r, L, R);
return res;
}
#undef ls
#undef rs
}st;//segment tree
struct SAM {
struct node {
int maxl, fa, son[26];
node() {
maxl = fa = 0;
memset(son, 0, sizeof(son));
}
node operator&=(const node& ots) {
maxl = ots.maxl, fa = ots.fa;
memcpy(son, ots.son, sizeof(ots.son));
return *this;
}
}step[(N << 1) + 10];
int root, last, tot;
SAM() { root = last = tot = 1; }
void insert(int ch) {
int p = last, np = ++tot;
step[np].maxl = step[p].maxl + 1;
while (p && !step[p].son[ch]) {
step[p].son[ch] = np;
p = step[p].fa;
}
if (!p) step[np].fa = root;
else {
int q = step[p].son[ch];
if (step[q].maxl != step[p].maxl + 1) {
int nq = ++tot;
step[nq] = step[q];
step[nq].maxl = step[p].maxl + 1;
step[np].fa = step[q].fa = nq;
while (p && step[p].son[ch] == q) {
step[p].son[ch] = nq;
p = step[p].fa;
}
}
else step[np].fa = q;
}
last = np;
}
ll solve(char* s, int m) {
int len = strlen(s + 1), p = root, L = 0;
ll Ans = 0;
for (int i = 1; i <= len; i++) {
while (!step[p].son[s[i] - 'a'] && p != root)
p = step[p].fa, L = step[p].maxl;
if (step[p].son[s[i] - 'a'])
p = step[p].son[s[i] - 'a'], L++;
Ans = max(Ans, st.Query(1, 1, m, i - L + 1, i).now);
}
return Ans;
}
}sam;
char s[N + 10];
int main() {
int n = read(0), m = read(0), k = read(0);
UNUSED(k);
scanf("%s", s + 1);
for (int i = 1; i <= n; i++) sam.insert(s[i] - 'a');
for (int i = 1; i <= m; i++)
V[i] = read(0);
st.build(1, 1, m);
for (int i = 1; i <= k; i++) {
scanf("%s", s + 1);
printf("%lld\n", sam.solve(s, m));
}
return 0;
}
I.Ice Drinking
记\(x\)为一个随机\(n\)阶排列中\(a_i=i\)的个数,求\(x^k\)的期望值
数论难难,咕咕咕
J.Journey
一个城市中有若干个十字路口,右转不需要等红灯,直行左转掉头都需要等红灯,求起点到终点最少需要等多少次红灯?
其实是个裸的最短路,但本题建图的方式稍有区别
本题为路口编号,道路则由\(<a,b>\)的形式给出。故我们将道路看做点,点与点之间的权值为是否需要等红灯,只要将图建好即可跑最短路了
/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<unordered_set>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
int f = 1; char ch = getchar();
for (; ch < '0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
const ll N = 5e9;
template<typename T>
class MyArray {
private:
T* S;
int Msize;
void expandSize();
public:
MyArray(int size = 1) { S = new T[Msize = size]; }
~MyArray() { delete[] S; }
MyArray(const MyArray& tis) = delete;
MyArray& operator=(const MyArray& tis);
bool operator !()const { return !Msize; }
T& operator [](int index);
};
template<typename T>
void MyArray<T>::expandSize() {
T* temp = S;
S = new T[Msize << 1];
for (int i = 0; i < Msize; i++)
S[i] = temp[i];
Msize <<= 1;
delete[] temp;
}
template<typename T>
MyArray<T>& MyArray<T>::operator=(const MyArray& tis) {
S = new T[Msize = tis.Msize];
for (int i = 0; i < tis.Msize; i++)
S[i] = tis.S[i];
return *this;
}
template<typename T>
T& MyArray<T>::operator [](int index) {
while (index >= Msize)
expandSize();
return S[index];
}
MyArray<int>pre, now, child, val;
int tot;
void link(int x, int y, int v) { pre[++tot] = now[x], now[x] = tot, child[tot] = y, val[tot] = v; }
unordered_map<int, int>Dis;
unordered_map<ll, int>Point;
unordered_map<int, bool > Vis;
struct node {
int x, v;
node(int _x = 0, int _v = 0) { x = _x, v = _v; }
bool operator <(const node& ots)const { return v > ots.v; }
};
void Dijkstra(int S, int T) {
priority_queue<node>Heap;
Heap.push(node(S, Dis[S] = 0));
while (!Heap.empty()) {
node Now = Heap.top(); Heap.pop();
if (Vis[Now.x]) continue;
Vis[Now.x] = 1;
for (int p = now[Now.x]; p; p = pre[p]) {
int son = child[p];
if (Dis[son] > Dis[Now.x] + val[p]) {
Dis[son] = Dis[Now.x] + val[p];
Heap.push(node(son, Dis[son]));
}
}
}
printf("%d\n", Dis[T] >= iMax ? -1 : Dis[T]);
}
int main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n = read(0), cnt = 0;
for (int i = 1; i <= n; i++) {
vector<int>c(4, 0);
for (int k = 0; k < 4; k++) {
c[k] = read(0);
if (c[k]) {
if (!Point[N * i + c[k]]) {
Point[N * i + c[k]] = ++cnt;
now[cnt] = 0;
}
if (!Point[N * c[k] + i]) {
Point[N * c[k] + i] = ++cnt;
now[cnt] = 0;
}
}
}
for (int a = 0; a < 4; a++) {
for (int b = 0; b < 4; b++) {
if (!c[a] || !c[b]) continue;
if ((a + 1) % 4 != b)
link(Point[N * c[a] + i], Point[N * i + c[b]], 1);
else
link(Point[N * c[a] + i], Point[N * i + c[b]], 0);
}
}
}
int s1 = read(0), s2 = read(0), t1 = read(0), t2 = read(0);
int S = Point[N * s1 + s2], T = Point[N * t1 + t2];
for (int i = 1; i <= cnt; i++) Dis[i] = iMax;
Dijkstra(S, T);
return 0;
}