目录
6194 string string string 后缀自动机 - 求恰好出现k次的字符串
#include <bits/stdc++.h>
int k;
using namespace std;
typedef long long ll;
ll res1, res2;
namespace Sam { // 后缀自动机 板子
const int M = 1e5 + 1;
const int CHAR_NUM = 26;
// 后缀自动机
struct SAM {
struct state {
int len, link, cnt;
vector<int> nxt;
state() { nxt.resize(CHAR_NUM); }
} st[M * 2];
int sz;//自动机的大小
int last;// 未加入下一个字符前最长的前缀(整个串)所属的节点的编号
void clear(vector<int> &a) {
for (int i = 0; i < CHAR_NUM; i++) {
a[i] = 0;
}
}
void init() {
// 创建只有一个初始状态的sam
st[0].len = 0;
st[0].link = -1;
st[0].cnt = 0;
clear(st[0].nxt);
sz = 1;
last = 0;
}
ll insert(int c, int k) {
int cur = sz++;
st[cur].cnt = 0;
st[cur].len = st[last].len + 1;
clear(st[cur].nxt);
st[cur].link = -1;
int p = last;
while (p != -1 && !st[p].nxt[c]) {
st[p].nxt[c] = cur;
p = st[p].link;
}
if (p == -1) {
st[cur].link = 0;
} else {
int q = st[p].nxt[c];
if (st[p].len + 1 == st[q].len) {
st[cur].link = q;
} else {
int clone = sz++;//复制
st[clone].len = st[p].len + 1;
st[clone].nxt = st[q].nxt;
st[clone].link = st[q].link;
st[clone].cnt = st[q].cnt;
while (p != -1 && st[p].nxt[c] == q) {
st[p].nxt[c] = clone;
p = st[p].link;
}
st[q].link = st[cur].link = clone;
}
}
last = cur;
int now = last;
ll res = 0;
while (now != -1) {
if (st[now].cnt >= k + 1) {
break;
}
st[now].cnt++;
if (st[now].cnt == k) {
res1 += st[now].len - st[st[now].link].len;
}
if (st[now].cnt == k + 1) {
res2 += st[now].len - st[st[now].link].len;
}
now = st[now].link;
}
return res;
}
} sam;
}
using namespace Sam;
string s;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin >> T;
for (int cs = 1; cs <= T; cs++) {
cin >> k;
cin >> s;
int len = s.length();
sam.init();
res1 = 0, res2 = 0;
for (int i = 0; i < len; i++) {
res1 += sam.insert(s[i] - 'a', k);
}
cout << res1 - res2 << endl;
}
return 0;
}
6195 cable cable cable
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
ll n, k;
while (~scanf("%lld%lld", &n, &k)) {
printf("%lld\n", k + k * (n - k));
}
return 0;
}
6196 happy happy happy
6196 happy happy happy
6197 array array array 最长非递减子序列
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int inf = 0x3f3f3f3f;
int b[MAXN];
int a[MAXN];
int dp[MAXN];
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)scanf("%d", &a[i]), dp[i] = inf;
for (int i = 1; i <= n; ++i) {
*lower_bound(dp + 1, dp + i, a[i]) = a[i];
}
int len1 = lower_bound(dp + 1, dp + n + 1, inf) - dp - 1;
int nn = n;
for (int i = 1; i <= n; i++)b[i] = a[nn--], dp[i] = inf;
for (int i = 1; i <= n; ++i) {
*lower_bound(dp + 1, dp + i, b[i]) = b[i];
}
int len2 = lower_bound(dp + 1, dp + n + 1, inf) - dp - 1;
if (len1 >= n - k || len2 >= n - k)
printf("A is a magic array.\n");
else
printf("A is not a magic array.\n");
}
return 0;
}
6198 number number number 找规律 + 线性递推式 + 矩阵快速幂
根据前20个斐波那契数,暴力打表找到前5个数:4、12、33、88、232
再暴力打表得出任意一个满足要求的线性递推式:
F n = 3 F n − 1 − F n − 2 + 1 ( F 0 = 4 , F 1 = 12 ) F_n=3F_{n-1}-F_{n-2}+1 (F_0=4 ,F_1=12) Fn=3Fn−1−Fn−2+1(F0=4,F1=12)
再转化成矩阵形式:
[
F
n
F
n
−
1
1
]
=
[
3
−
1
1
1
0
0
0
0
1
]
×
[
F
n
−
1
F
n
−
2
1
]
\begin{bmatrix} F_n\\F_{n-1}\\1\end{bmatrix}=\begin{bmatrix}3 &-1 & 1\\1&0&0\\0&0&1\end{bmatrix} \times \begin{bmatrix} F_{n-1}\\F_{n-2}\\1\end{bmatrix}
⎣⎡FnFn−11⎦⎤=⎣⎡310−100101⎦⎤×⎣⎡Fn−1Fn−21⎦⎤
直接上矩阵快速幂板子
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
namespace Matrix {//矩阵快速幂板子
const int maxn = 3;
ll mod = 998244353;
struct Mat {
ll mat[maxn][maxn];
void clear() {
memset(mat, 0, sizeof(mat));
}
Mat operator*(const Mat a) const {
Mat b;
b.clear();
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
for (int k = 0; k < maxn; ++k) {
b.mat[i][j] = (b.mat[i][j] + (mat[i][k] * a.mat[k][j]) % mod + mod) % mod;
}
}
}
return b;
}
};
Mat pow(Mat m, ll k) {
Mat res;
res.clear();
for (int i = 0; i < maxn; ++i) {//单位矩阵
res.mat[i][i] = 1;
}
while (k) {
if (k & 1)res = res * m;
k >>= 1;
m = m * m;
}
return res;
}
Mat E;//转移矩阵
Mat f;
void init() {
E.clear();
E = {
3, -1, 1,
1, 0, 0,
0, 0, 1
};
f = {
12, 0, 0,
4, 0, 0,
1, 0, 0
};
}
}
using namespace Matrix;
int n;
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
while (cin >> n) {
init();
if (n == 1) cout << 4 << endl; // k=1 对应 F[0]
else if (n == 2) cout << 12 << endl;
else {
cout << (pow(E, n - 2) * f).mat[0][0] << endl;
}
}
return 0;
}
6199 gems gems gems
6199 gems gems gems
6200 mustedge mustedge mustedge · lct
6200 mustedge mustedge mustedge
洛谷 P2542 [AHOI2005] 航线规划 的翻版:
不存在删边情况,
当有双连通分量出现,连通内部任意两个点之间的关键路径均为0(有环就有两条路可以走),所以可以将整个连通分量缩成一个点,
关键路径数 = 缩点后剩余点数 - 1
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, m, k;
namespace LCT { // lct板子
int h[N];// 缩点后x所在的点 类似并查集的f[]数组
const int mod = 51061;
int w[N];// w[x]表示x的点权
int sz[N];//sz[x] 表示以x为根的splay树的大小
int rev[N];//rev为区间翻转懒标记数组
int f[N];// 父节点
int st[N];//栈
int c[N][2];// 每个节点的两个儿子
// 判断节点x是否为一个splay的根 如果不是根 返回true
inline bool nRoot(register int x) {
// 如果节点x是根 则其与其父亲节点之间连的是轻边
// 如果连的是轻边 他的父亲的儿子里没有它
return c[f[x]][0] == x || c[f[x]][1] == x;
}
#define lc c[x][0]
#define rc c[x][1]
// splay区间翻转操作
inline void reverse(register int x) {
swap(lc, rc);
rev[x] ^= 1;
}
// 判断并释放懒标记
inline void pushDown(register int x) {
if (rev[x]) {
swap(lc, rc);
rev[lc] ^= 1, rev[rc] ^= 1;
rev[x] = 0;
}
}
// 更新信息
inline void pushUp(register int x) {
w[x] = w[lc] + w[rc] + 1;
}
// 一次旋转
inline void rotate(register int x) {
register int y = f[x], z = f[y], k = (c[y][1] == x), w = c[x][!k];
if (nRoot(y))
c[z][c[z][1] == y] = x;
c[x][!k] = y;
c[y][k] = w;
if (w)
f[w] = y;
f[y] = x;
f[x] = z;
pushUp(y);//原来的父亲现在是儿子了 更新新儿子的信息 而新父亲的信息不是在这里更新的
}
// 旋转一颗splay树 改为以x为根节点
inline void splay(register int x) {
register int y = x, z = 0;
st[++z] = y;//暂存当前点到根的整条路径 pushdown时一定要从上往下放标记
while (nRoot(y)) st[++z] = y = f[y];
while (z) pushDown(st[z--]);
//然后再从下往上旋转 splay的基本操作
while (nRoot(x)) {
y = f[x];
z = f[y];
if (nRoot(y))
rotate((c[y][0] == x) ^ (c[z][0] == y) ? x : y);
rotate(x);
}
pushUp(x);//更新最终新父亲x的信息
}
inline int geth(register int x) {
return x == h[x] ? x : h[x] = geth(h[x]);
}
// 打通当前根节点到指定节点的树链
// 使得一条中序遍历以根开始、以指定点结束的splay出现
inline void access(register int x) {
for (int y = 0; x; y = x, x = f[x] = geth(f[x])) {
splay(x);
rc = y;
pushUp(x);
}
}
//换根
inline void makeRoot(register int x) {
access(x);
splay(x);
rev[x] ^= 1;
}
//提取路径 拉出x->y的路径成为一个splay 然后以y为splay的根
inline void split(register int x, register int y) {
makeRoot(x);
access(y);
splay(y);
}
// 查找在树的根
// 因为lct要求满足中序遍历Splay得到的每个点的深度序列严格递增 根的深度最小
// 一直往左儿子就是了
int findRoot(register int x) {
access(x);
splay(x);
while (lc) {
pushDown(x);
x = lc;
}
// 现在x是root
splay(x);
return x;
}
// 连边
inline void link(register int x, register int y) {
makeRoot(x);
if (findRoot(y) != x) {
f[x] = y;
}
}
// 断边
inline void cut(register int x, register int y) {
makeRoot(x);
if (findRoot(y) == x && f[y] == x && !c[y][0]) {
f[y] = c[x][1] = 0;
pushUp(x);
}
}
// 判断 x 和 y 是否连通
bool isConnected(int x, int y) {
return findRoot(x) == findRoot(y);
}
inline void del(register int x, register int y) {
if (x) {
h[x] = y;
del(lc, y);
del(rc, y);
}
}
//双连通分量存在时 连接x和y
inline void merge(register int x, register int y) {
if (x == y) return;
makeRoot(x);
if (findRoot(y) != x) {
f[x] = y;// 等于 link(x,y)
return;
}
// 双连通分量 暴力缩点
del(rc, x);// 将以x为根的splay树缩成一个点 都用x来表示
rc = 0;
pushUp(x);//缩点 删点
}
void init(int n) {
for (int i = 0; i <= n; i++) {
f[i] = c[i][0] = c[i][1] = 0;
w[i] = 1;
h[i] = i;
}
}
}
using namespace LCT;
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T, u, v, q, op;
cin >> T;
for (int cs = 1; cs <= T; cs++) {
cout << "Case #" << cs << ":\n";
cin >> n >> m;
for (int i = 1; i <= n; i++) {
f[i] = c[i][0] = c[i][1] = 0;
w[i] = 1, h[i] = i;
}
for (int i = 1; i <= m; i++) {
cin >> u >> v;
merge(geth(u), geth(v));
}
cin >> q;
while (q--) {
cin >> op >> u >> v;
if (op == 1) {
merge(geth(u), geth(v));
} else {
u = geth(u), v = geth(v);
split(u, v);
cout << w[v] - 1 << endl;
}
}
}
return 0;
}
6201 transaction transaction transaction 树形dp + 换根
hdu 6201 transaction transaction transaction
取树上某两点为起点和终点,问这条树链的权值最大是多少
只想到了树形dp+换根:根节点为终点,在树里找最小值所在对应的起点
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = (1ll << 45);
const int N = 2e5 + 10;
ll a[N], d[N];
int n;
struct Edge {
int v;
ll w;
};
vector<Edge> e[N];
ll dp[N][2];//dp[i][0] dp[i][1] 表示以节点i为根的子树里的最小花费 和 次小花费 不包含在i点买书的消费
int son[N][2];// dp[i][0] dp[i][1] 对应的儿子节点
void dfs1(int u, int f, ll dep) {
d[u] = dep;
for (auto x:e[u]) {
int v = x.v;
if (v != f) {
dfs1(v, u, dep + x.w);
ll tmp = -dep - x.w - a[v]; //dp[v][0]里不包含在v点买书的情况 所以要补上
tmp = max(tmp, dp[v][0]);
if (tmp >= dp[u][0]) {
dp[u][1] = dp[u][0];
son[u][1] = son[u][0];
dp[u][0] = tmp;
son[u][0] = v;
} else if (tmp > dp[u][1]) {
dp[u][1] = tmp;
son[u][1] = v;
}
}
}
}
ll ans[N];
//换根
void dfs2(int u, int f, ll Max) {
Max = max(Max, -a[u]);// u方向传来的最小花费 Max必定为负值
for (auto x:e[u]) {
int v = x.v;
if (v != f) {//以v为出售点 即 以v为新的根
ll tmp1 = Max - x.w;// 根节点方向传来的最小花费
ll tmp2 = son[u][0] == v ?
dp[u][1] + d[u] - x.w : dp[u][0] + d[u] - x.w;// u的子树方向传来的最小花费
// 特判最小花费既在u的子树里 又在v的子树里
ll tmp3 = dp[v][0] + d[v];// v的子树里传来的最小花费
ans[v] = a[v] + max(tmp1, max(tmp2, tmp3));
dfs2(v, u, max(tmp1, tmp2)); //更新Max = Max-x.w = max(tmp1, tmp2)
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
dp[0][0] = dp[0][1] = -inf;
int T;
cin >> T;
for (int cs = 1; cs <= T; cs++) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
dp[i][0] = dp[i][1] = -inf;
son[i][0] = son[i][1] = 0;
}
for (int i = 1, u, v, w; i < n; i++) {
cin >> u >> v >> w;
e[u].push_back({v, w});
e[v].push_back({u, w});
}
dfs1(1, 0, 0);
ll res = a[1] + dp[1][0];
dfs2(1, 0, -a[1]);//初始时 最小花费为节点1买书的消费
for (int i = 2; i <= n; i++) {
res = max(res, ans[i]);
}
res = max(res, 0ll);
cout << res << endl;
for (int i = 1; i <= n; i++) {
e[i].clear();
}
}
return 0;
}
6202 cube cube cube
6202 cube cube cube
6203 ping ping ping · lca + 树链剖分
// 未ac代码 - hdu 瘫了
#include <bits/stdc++.h>
using namespace std;
template<class T>
void _print(T arg) {
cout << arg << " ";
}
template<class... Args>
void log(Args... args) {
int arr[] = {(_print(args), 0)...};
cout << endl;
}
#define between(x, a, b) (a<=x && x<=b)
typedef unsigned long long ull;
typedef long long ll;
const int N = 1e4 + 10;
const int M = 1e5 + 10;
vector<int> e[N];
int n, m, k;
int dep[N];
namespace LCA {
int Log[N], fa[N][20];
void initLog() {
Log[0] = -1, Log[1] = 0;
for (int i = 2; i < N; i++) {
Log[i] = Log[i / 2] + 1;
}
}
void dfs(int u, int f) {
dep[u] = dep[f] + 1;
fa[u][0] = f;
for (int i = 1; (1 << i) <= n; i++) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
}
for (int v:e[u]) {
if (v != f) {
dfs(v, u);
}
}
}
int lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
while (dep[u] > dep[v])
u = fa[u][Log[dep[u] - dep[v]]];
if (u == v) return v;
for (int i = Log[dep[u]]; i >= 0; i--) {
if (fa[u][i] != fa[v][i]) {
u = fa[u][i];
v = fa[v][i];
}
}
return fa[u][0];
}
}
using namespace LCA;
struct segTree {
#define ls (o<<1)
#define rs (o<<1|1)
struct node {
int l, r;
int lazy;
} t[N << 2];
void build(int o, int l, int r) {
t[o] = {l, r, 0};
if (l >= r) return;
int mid = l + r >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
}
int query(int o, int L, int R) {
if (L <= t[o].l && t[o].r <= R) {
return t[o].lazy;
}
int mid = t[o].l + t[o].r >> 1;
if (L > mid) return query(rs, L, R);
else if (R <= mid) return query(ls, L, R);
else return query(ls, L, mid) + query(rs, mid + 1, R);
}
void pushUp(int o) {
t[o].lazy = t[ls].lazy + t[rs].lazy;
}
void update(int o, int pos) {
if (t[o].l == t[o].r) {
t[o].lazy = 1;
return;
}
int mid = t[o].l + t[o].r >> 1;
if (pos <= mid) update(ls, pos);
else update(rs, pos);
pushUp(o);
}
} ST;
namespace TreeChain {
int f[N], son[N], sz[N];
void dfs1(int u, int fa) {
f[u] = fa;
//dep[u] = dep[fa] + 1;
sz[u] = 1;
son[u] = 0;
for (int v:e[u]) {
if (v != fa) {
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > sz[son[u]]) {
son[u] = v;
}
}
}
}
int dfn = 0, id[N], top[N];
void dfs2(int u, int tp) {
id[u] = ++dfn;
top[u] = tp;
if (son[u]) dfs2(son[u], tp);
for (int v:e[u]) {
if (v != f[u] && v != son[u]) {
dfs2(v, v);
}
}
}
void init() {
dfn = 0;
}
//判断树链上是否有标记过的点
bool isExitence(int u, int v) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
if (ST.query(1, id[top[u]], id[u])) return true;
u = f[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
return ST.query(1, id[u], id[v]) != 0;
}
}
using namespace TreeChain;
struct Que {
int u, v, fa;// f=lca(u,v)
void input() {
cin >> u >> v;
u++, v++;
fa = lca(u, v);
}
bool operator<(const Que b) {
return dep[fa] > dep[b.fa];
}
} a[M];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("1.in", "r", stdin);
freopen("debug.out", "w", stdout);
#endif
initLog();
while (cin >> n) {
for (int i = 1, u, v; i <= n; i++) {
cin >> u >> v;
u++, v++;
e[u].push_back(v);
e[v].push_back(u);
}
n++;
// lca init
dfs(1, 0);
// TreeChain init
dfn = 0;
dfs1(1, 0);
dfs2(1, 1);
cin >> m;
for (int i = 1; i <= m; i++) {
a[i].input();
}
sort(a + 1, a + 1 + m);
ST.build(1, 1, n);
int res = 0;
for (int i = 1; i <= m; i++) {
int u = a[i].u, v = a[i].v;
if (isExitence(u, v)) continue;
ST.update(1, id[a[i].fa]);
res++;
}
cout << res << endl;
for (int i = 0; i <= n; i++) {
e[i].clear();
}
}
return 0;
}
6204 triangulation triangulation triangulation
6204 triangulation triangulation triangulation
6205 card card card
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e6 + 5;
typedef long long ll;
int a[MAXN];
int b[MAXN], c[MAXN];
int main() {
int n;
while (~scanf("%d", &n)) {
for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)scanf("%d", &b[i]);
for (int i = 1; i <= n; i++)c[i] = a[i] - b[i];
int id = 0;
for (int i = n + 1; i <= 2 * n; i++) {
a[i] = a[++id];
b[i] = b[id];
c[i] = c[id];
}
int sum = 0;
int maxn = 0;
int ansmaxn = 0;
int ansl = 0;
int l = 1;
for (int i = 1; i <= 2 * n; i++) {
maxn += a[i];
sum += c[i];
if (sum == 0) {
if (ansmaxn < maxn) {
ansmaxn = maxn;
ansl = l;
}
}
if (sum < 0) {
sum = 0;
maxn = 0;
l = i + 1;
}
}
printf("%d\n", l - 1);
}
return 0;
}