[题解]小游还在卷,小卷同小游,小狗刨着游
\(\mathcal{Back\;To\;The\;Menu}\).
2022-03-01 小游还在卷,小卷同小游,小狗刨着游
不敢相信 \(\log^3\) 的算法可以过 \(10^5\) 的数据,这就像 \(10^6\) 成为分块的数据范围一样惊人!
排队 / Queue
关键的观察:一个数字放在比它大的数字旁边,它将不会成为 asshole,否则,它将一定是 asshole.
然后就可以经典的连续段 DP,从大往小放数,设 \(f(i,j,k)\) 表示放了 \([i,n]\),有 \(j\) 个连续段,有 \(k\) 个数字的方案数,转移很简单:
- \(f(i,j,k)\times (j+1)\to f(i-1,j+1,k+1)\);
- \(f(i,j,k)\times (j-1)\to f(i-1,j-1,k)\);
- \(f(i,j,k)\times (2j)\to f(i-1,j,k)\);
直接转移是 \(\mathcal O(n^2m)\) 的,但是发现始终有 \(j\le k\),就可以 \(\mathcal O(nm^2)\) 做了,再一看,发现转移和 \(i\) 没关系,都是 \(j\) 在那里瞎乘之后转移到下一个状态,而 \(n\) 打得离谱,不难导向矩阵优化 DP 转移,最后的复杂度就是大概 \(\mathcal O((m^2)^3\log n)\),但是实际矩阵大小可以比 \(m^2\) 稍小一些,时间复杂度就没有这么卡。
/** @author __Elaina__ */
#include <bits/stdc++.h>
using namespace std;
#define USING_FREAD
// #define NDEBUG
#include <cassert>
namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */
#define rep(i, l, r) for (int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for (int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
template <class T>
inline T fab(T x) {
return x < 0 ? -x : x;
}
template <class T>
inline void chkmin(T& x, const T rhs) {
x = std::min(x, rhs);
}
template <class T>
inline void chkmax(T& x, const T rhs) {
x = std::max(x, rhs);
}
template <class T>
inline void myswap(T& x, T& y) {
x ^= y ^= x ^= y;
}
#ifdef USING_FREAD
inline char qkgetc() {
#define BUFFERSIZE 1 << 20
static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
#undef BUFFERSIZE
}
#define CHARRECEI qkgetc()
#else
#define CHARRECEI getchar()
#endif
template <class T>
inline T readret(T x) {
x = 0;
int f = 0;
char c;
while (!isdigit(c = CHARRECEI))
if (c == '-')
f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48))
;
return f ? -x : x;
}
template <class T>
inline void readin(T& x) {
x = 0;
int f = 0;
char c;
while (!isdigit(c = CHARRECEI))
if (c == '-')
f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48))
;
if (f)
x = -x;
}
template <class T, class... Args>
inline void readin(T& x, Args&... args) {
readin(x), readin(args...);
}
template <class T>
inline void writln(T x, char c = '\n') {
if (x < 0)
putchar('-'), x = -x;
static int __stk[55], __bit = 0;
do
__stk[++__bit] = x % 10, x /= 10;
while (x);
while (__bit) putchar(__stk[__bit--] ^ 48);
putchar(c);
}
} // namespace Elaina
using namespace Elaina;
const int Maxm = 70; ///< maybe 66 is enough?
ll n;
int m, mod, cnt;
inline void chkadd(int& x, int y) {
if ((x += y) >= mod)
x -= mod;
}
struct matrix {
int a[Maxm + 5][Maxm + 5];
inline matrix() { memset(a, 0, sizeof a); }
inline matrix operator*(const matrix& rhs) {
matrix ret;
for (int i = 0; i < cnt; ++i)
for (int j = 0; j < cnt; ++j) {
for (int k = 0; k < cnt; ++k) chkadd(ret.a[i][k], a[i][j] * rhs.a[j][k] % mod);
}
return ret;
}
};
inline matrix qkpow(matrix a, ll q) {
matrix ret;
for (int i = 0; i < cnt; ++i) ret.a[i][i] = 1;
for (; q; q >>= 1, a = a * a)
if (q & 1)
ret = ret * a;
return ret;
}
int id[Maxm + 5][Maxm + 5]; ///< the dimension of a status
matrix trans;
signed main() {
freopen("queue.in", "r", stdin);
freopen("queue.out", "w", stdout);
readin(n, m, mod);
for (int i = 0; i <= m; ++i)
for (int j = i; j <= m; ++j) id[i][j] = cnt++;
// fprintf(stderr, "cnt == %d\n", cnt);
for (int i = 0; i <= m; ++i)
for (int j = i; j <= m; ++j) {
if (j < m)
trans.a[id[i][j]][id[i + 1][j + 1]] = i + 1; // add a new segment
if (i > 0)
trans.a[id[i][j]][id[i - 1][j]] = i - 1; // merge two segments
trans.a[id[i][j]][id[i][j]] = i << 1; // place the number at a side of a segment
}
// for (int i = 0; i < cnt; ++i) for (int j = 0; j < cnt; ++j)
// fprintf(stderr, "a[%d, %d] == %d\n", i, j, trans.a[i][j]);
trans = qkpow(trans, n);
writln(trans.a[id[0][0]][id[1][m]]);
return 0;
}
/**
*
* key observation: if i is not close to all the value which bigger
* than i(refer to the number belongs to (i, n]), it'll be an asshole.
*
* ==> consecutive segment DP:
*
* let f(i, j, k) be that we've finished filling number i, have j segment in total, there're k assholes.
*
* the transition is easy, only have to discuss the position the number will fill in.
*
* the comlexity is O(mn^2)
*
* note that when j is increasing, k will increase synchronously, so useful j <= 10
*
* --> O(nm^2) is 60% !
*
* pay attention that the transition is fixed, so matrix-optimization is available!!!
*
* -> O(m^6 logn) is 100% ! (constant warning!)
*
*/
昵称 / Nickname
显然最终的答案长度不会超过 \(|S|+C\),这个 \(C\) 是一个很小的常数。我们最后要构建答案,显然需要求出一个类似于 \(f(i,c)\) 表示长度为 \(i\),该位填的是 \(c\) 且含有 \(S\) 的串的方案数。一个很经典的想法是构建自动机,设 \(to(i,c)\) 表示当前匹配到 \(S\) 的第 \(i\) 位,填入字符 \(c\) 之后最长可以匹配到 \(S\) 的哪一位,显然配合 kmp 可以求出这个 \(to\),注意特殊情况是 \(to(n,c)=n(c=0,1,2,\cdots,9)\) 意为已经完成匹配。
那么我们要求的目标数组也很清晰了,设 \(f(i,u)\) 表示当前的串长度为 \(i\),匹配到 \(S\) 的第 \(u\) 位时,包含 \(S\) 的串个数有多少,转移就枚举当前这一位填什么就行了,边界情况是 \(to(|S|+C,n)=1\).
最后构建答案的时候也枚举一下这一位应当填什么就行了。
代码中的 \(f\) 是倒着定义的。
只是一个后记......
后来又想了一下这个题,其实从某种意义上说它就是一个 DFA 上的 DP,实际上 $to()$ 就是这个 DFA,它接受所有含有串 $S$ 的串,而状态 $f(i, j)$ 的定义实际上就是还剩长度为 $i$,在自动机上的位置为 $j$ 时,最终能够走到 $n$ 号点(被接受)的方案数,这样对这道题的说明更加深刻一些。
/** @author __Elaina__ */
#include <bits/stdc++.h>
using namespace std;
#define USING_FREAD
// #define NDEBUG
#include <cassert>
namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */
#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void chkmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void chkmax(T& x, const T rhs) { x = std::max(x, rhs); }
template<class T> inline void myswap(T& x, T& y) { x ^= y ^= x ^= y; }
#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif
template<class T> inline T readret(T x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
return f ? -x : x;
}
template<class T> inline void readin(T& x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
if (x < 0) putchar('-'), x = -x;
static int __stk[55], __bit = 0;
do __stk[++__bit] = x % 10, x /= 10; while (x);
while (__bit) putchar(__stk[__bit--] ^ 48);
putchar(c);
}
} // namespace Elaina
using namespace Elaina;
const int Maxn = 1000;
const int Sigma = 10;
const ll threshold = 1e18;
inline void chkadd(ll& a, ll b) {
if ((a += b) >= threshold) a = threshold;
}
char s[Maxn + 5];
int a[Maxn + 5], n;
ll rnk;
inline void input() {
cin >> s + 1 >> rnk;
n = strlen(s + 1);
rep (i, 1, n) a[i] = s[i] ^ 48;
}
int nxt[Maxn + 5];
int to[Maxn + 5][Sigma + 5]; ///< transfer array
inline void prelude() {
rep (i, 1, n) {
nxt[i] = nxt[i - 1];
while (nxt[i] && a[nxt[i] + 1] != a[i]) nxt[i] = nxt[nxt[i]];
if (nxt[i] + 1 < i && a[nxt[i] + 1] == a[i]) ++nxt[i];
}
rep (i, 0, n) {
rep (c, 0, Sigma - 1) {
if (i == n) { to[i][c] = n; continue; }
int k = i;
while (k && a[k + 1] != c) k = nxt[k];
if (a[k + 1] == c) ++k;
to[i][c] = k;
}
}
}
ll f[Maxn + 50 + 5][Maxn + 5];
inline void runDp() {
f[0][n] = 1;
rep (i, 1, n + 50) for (int j = 0; j <= n; ++j)
rep (c, 0, Sigma - 1) {
chkadd(f[i][j], f[i - 1][to[j][c]]);
// fprintf(stderr, "f[%d, %d] == %lld\n", i, j, f[i][j]);
}
}
int ans[Maxn + 50 + 5];
inline void getAns() {
int p = 0;
for (int i = n + 50; i > 0; --i) {
for (int c = 0; c < Sigma; ++c) {
// fprintf(stderr, "i == %d, j == %d, rnk == %lld, f[%d, %d] == %lld\n", i, c, rnk, i - 1, to[p][c], f[i - 1][to[p][c]]);
if (rnk > f[i - 1][to[p][c]]) rnk -= f[i - 1][to[p][c]];
else {
ans[i] = c, p = to[p][c];
break;
}
}
}
int hd = n + 50;
while (hd > 1 && !ans[hd]) --hd;
while (hd) printf("%d", ans[hd--]);
}
signed main() {
freopen("nickname.in", "r", stdin);
freopen("nickname.out", "w", stdout);
cin.tie(NULL)->sync_with_stdio(false);
input();
prelude();
runDp();
getAns();
return 0;
}
/**
*
* the final length is less than |S|+20 (approximately)
*
* so enumerate the final length, and now the question is:
* how many numbers of number who include S and the length of which is L
* BIT DP:
* define dfs(i, j): now at bit @p i , and the matched position is @p j
* presolve the array @p nxt[] and @p to[] first, the complexity is O(|S|^2 * 10 * const)
* (constant warning!)
*
*/
帝国防卫 / Empire
\(\log^3\) 爆切 \(10^5\) 范围。
既然复杂度都这么暴力,显然思路十分简单,注意到对于某子树来说,除去儿子上来的情况,对于某一深度的修改的值都是完全相同的,因此可以根据 BFS 序建线段树维护修改,而每次 \(a\) 只会蔓延最多 \(\log\) 层,因此总复杂度为 \(\mathcal O(q\log^3 n)\) 的。
永神提出可以使用 CDQ 分治加虚树做到 \(\mathcal O(q\log^2 n)\).
/** @author __Elaina__ */
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define USING_FREAD
// #define NDEBUG
#include <cassert>
namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */
#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void chkmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void chkmax(T& x, const T rhs) { x = std::max(x, rhs); }
template<class T> inline void myswap(T& x, T& y) { x ^= y ^= x ^= y; }
#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif
template<class T> inline T readret(T x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
return f ? -x : x;
}
template<class T> inline void readin(T& x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
if (x < 0) putchar('-'), x = -x;
static int __stk[55], __bit = 0;
do __stk[++__bit] = x % 10, x /= 10; while (x);
while (__bit) putchar(__stk[__bit--] ^ 48);
putchar(c);
}
} // namespace Elaina
using namespace Elaina;
const ll inf = 0x7fffffffffffffff;
const int Maxn = 1e5;
const int loga = 18;
int n;
int c[Maxn + 5];
vector<int> g[Maxn + 5];
inline void add_edge(int u, int v) {
g[u].push_back(v), g[v].push_back(u);
}
inline void input() {
readin(n);
rep (i, 1, n) readin(c[i]);
int u, v;
rep (i, 2, n) readin(u, v), add_edge(u, v);
}
queue<int> Q;
int bfn[Maxn + 5], bref[Maxn + 5];
inline void bfs() {
static int _vis[Maxn + 5] = {};
Q.push(1), _vis[1] = true;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
bref[bfn[u] = ++*bfn] = u;
for (const int& v: g[u]) if (!_vis[v])
Q.push(v), _vis[v] = true;
}
}
int fa[Maxn + 5], dfn[Maxn + 5], dep[Maxn + 5];
int bl[Maxn + 5][loga + 5], br[Maxn + 5][loga + 5];
int qr[Maxn + 5];
void prelude(int u, int par) {
memset(bl[u], 0x3f, sizeof bl[u]);
memset(br[u], 0xff, sizeof br[u]);
dfn[u] = ++*dfn, fa[u] = par, dep[u] = dep[par] + 1;
for (const int& v: g[u]) if (v ^ par) prelude(v, u);
qr[u] = *dfn;
int p = u, lev = 0;
while (p && dep[u] - dep[p] <= loga) {
chkmin(bl[p][lev], bfn[u]);
chkmax(br[p][lev], bfn[u]);
p = fa[p], ++lev;
}
}
struct bit {
#define lowbit(i) ((i) & (-(i)))
int c[Maxn + 5];
inline void modify(int i) {
for (; i <= n; i += lowbit(i)) ++c[i];
}
inline int query(int i) {
int ret = 0;
for (; i; i -= lowbit(i)) ret += c[i];
return ret;
}
inline int query(int l, int r) {
return query(r) - query(l - 1);
}
} Ans;
namespace saya {
ll mn[Maxn << 2 | 2], tag[Maxn << 2 | 2];
#define ls (i << 1)
#define rs (i << 1 | 1)
#define mid ((l + r) >> 1)
#define _lhs ls, l, mid
#define _rhs rs, mid + 1, r
#define _root int i = 1, int l = 1, int r = n
inline void pushup(int i) { mn[i] = min(mn[ls], mn[rs]); }
inline void sub(int i, ll v) { mn[i] -= v, tag[i] += v; }
inline void pushdown(int i) {
if (!tag[i]) return ;
sub(ls, tag[i]), sub(rs, tag[i]), tag[i] = 0;
}
void build(_root) {
if (l == r) return void(mn[i] = c[bref[l]]);
build(_lhs), build(_rhs), pushup(i);
}
void modify(int ql, int qr, ll v, _root) {
if (ql <= l && r <= qr && mn[i] > v) return sub(i, v);
if (l == r) {
Ans.modify(dfn[bref[l]]), mn[i] = inf;
return ;
}
pushdown(i);
if (ql <= mid) modify(ql, qr, v, _lhs);
if (mid < qr) modify(ql, qr, v, _rhs);
pushup(i);
}
#undef ls
#undef rs
#undef mid
#undef _lhs
#undef _rhs
#undef _root
}
inline void solve() {
saya::build();
int q = readret(1);
int op, x, a;
while (q--) {
readin(op, x);
if (op == 1) {
readin(a);
while (x && a) {
for (int lev = 0, v = a; v; ++lev, v >>= 1) {
if (!~br[x][lev]) break; // too deep!
saya::modify(bl[x][lev], br[x][lev], v - ((!!fa[x]) * v >> 2));
}
a >>= 1, x = fa[x];
}
} else writln(Ans.query(dfn[x], qr[x]));
}
}
signed main() {
freopen("empire.in", "r", stdin);
freopen("empire.out", "w", stdout);
input();
bfs();
prelude(1, 0);
solve();
return 0;
}