[题解]嘉然今天晚上吃点好的
\(\mathcal{Back\;To\;The\;Menu}\).
2022-03-03 嘉然今天晚上吃点好的
本来只给第二题分配了一个半小时,结果一直调到考试结束......
矩形 / Rectangle
建笛卡尔树,然后想怎么搞怎么搞。
/** @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 = 1e5;
const int Mod = 1e9 + 7;
const int inv2 = 500000004;
inline void chkadd(int& x, int y) { if ((x += y) >= Mod) x -= Mod; }
int h[Maxn + 5], w[Maxn + 5], n;
inline void input() {
readin(n);
rep (i, 1, n) readin(h[i]);
rep (i, 1, n) readin(w[i]);
}
int lson[Maxn + 5], rson[Maxn + 5];
int stk[Maxn + 5], tl;
inline void buildTree() {
rep (i, 1, n) {
while (tl && h[stk[tl]] >= h[i]) lson[i] = stk[tl--];
if (stk[tl]) rson[stk[tl]] = i;
stk[++tl] = i;
}
}
int f[Maxn + 5];
void dfs(int u) {
if (lson[u]) dfs(lson[u]), chkadd(f[u], f[lson[u]]);
if (rson[u]) dfs(rson[u]), chkadd(f[u], f[rson[u]]);
int tmp = 1ll * h[u] * (h[u] + 1) % Mod * inv2 % Mod;
if (lson[u]) chkadd(f[u], 1ll * w[lson[u]] * w[u] % Mod * tmp % Mod);
if (rson[u]) chkadd(f[u], 1ll * w[rson[u]] * w[u] % Mod * tmp % Mod);
if (lson[u] && rson[u])
chkadd(f[u], 1ll * w[lson[u]] * w[rson[u]] % Mod * tmp % Mod);
chkadd(f[u], 1ll * w[u] * (w[u] + 1) % Mod * inv2 % Mod * tmp % Mod);
chkadd(w[u], (w[lson[u]] + w[rson[u]]) % Mod);
}
signed main() {
freopen("rectangle.in", "r", stdin);
freopen("rectangle.out", "w", stdout);
input();
buildTree();
dfs(stk[1]);
writln(f[stk[1]]);
return 0;
}
[CEOI2020]星际迷航 / Tree
虽然一看就是矩阵加速......
如果 \(D=0\)(虽然没有),就是一个很明显的 DP 了,可以设 \(sg(i)\) 表示 \(i\) 点的 SG 函数值,那么有转移
当 \(D=1\),有了一个副本,而通过传送门走到下一个副本的点 \(x\),实际上就是以 \(x\) 为根做上面的东西,因此很明显我们需要换根。
有了上面的铺垫,你会发现接一个传送门,实际上是给原来的树上的某个点接上一个 \(sg\) 为 \(0\) 或者 \(1\) 的儿子,而该儿子在下一个副本将被视为根,也就是说,我们实际上关注的重点始终都是副本的根的状态,而不在意当点不为根时的状态,所以我们可以设计一个状态:\(f(i,0/1)\) 表示考虑到副本 \(i\),使得根的状态最终为 \(0/1\) 的方案数。
显然,求出初始的 \(f(D,0/1)\) 需要我们实现一个换根 DP,而从一个副本转移到另外一个副本呢?
由于 “接一个传送门,实际上是给原来的树上的某个点接上一个 \(sg\) 为 \(0\) 或者 \(1\) 的儿子”,而根据上面的 \(sg\) 转移,又发现接上一个 \(sg=1\) 的儿子好像对整棵树没啥影响,但是接上一个为 \(0\) 的儿子,对其父亲,乃至根可能都有影响。因此,我们对于转移有了一个大概的思路,先对当前的点最终状态是 \(0\) 还是 \(1\) 进行分类,假如当前的点最终是 \(0\),那么有两种情况:本身就是 \(0\),或者本来是 \(1\),但是树中的某个点翻转之后影响到它了,设 \(sg(u)\) 表示没有进行任何修改时,点 \(u\) 的 \(sg\) 函数值,那么就有
其中 \(flip(u)\) 为当 \(u\) 是根时,有多少原必败点翻转后会影响到它(你无法让必胜点翻转),而 \(\displaystyle Fp(t)=\sum_{sg(u)=t}flip(u)\),\(\displaystyle loss=\sum [sg(u)=0]\),同样,我们可以发现 \(f(i,1)\) 的转移:
其中 \(win\) 的定义和 \(loss\) 相反。
不难发现,转移中 \(Fp(),n,win,loss\) 这些系数都是固定的,因此可以直接矩阵加速转移,时间复杂度 \(\mathcal O(n+\log_2 D\times 2^3)\).
因为找不到草稿纸了所以不知道转移是否写错了,如有写错请指出。
/** @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;
// #define int long long
const int Mod = 1e9 + 7;
const int Maxn = 1e5;
inline void chkadd(int& x, int y) { if ((x += y) >= Mod) x -= Mod; }
int n; ll D;
vector<int> g[Maxn + 5];
struct matrix {
int c[2][2];
inline matrix() { memset(c, 0, sizeof c); }
inline matrix operator *(const matrix& rhs) {
matrix ret;
for (int i = 0; i < 2; ++i) for (int j = 0; j < 2; ++j)
for (int k = 0; k < 2; ++k)
chkadd(ret.c[i][k], 1ll * c[i][j] * rhs.c[j][k] % Mod);
return ret;
}
inline void print() {
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j)
writln(c[i][j], ' ');
Endl;
}
}
};
inline matrix qkpow(matrix a, ll q) {
matrix ret;
for (int i = 0; i < 2; ++i) ret.c[i][i] = 1;
for (; q; q >>= 1, a = a * a) if (q & 1) ret = ret * a;
return ret;
}
inline void input() {
readin(n, D);
int u, v;
rep (i, 2, n) {
readin(u, v);
g[u].push_back(v), g[v].push_back(u);
}
}
int sg[Maxn + 5], cnt[Maxn + 5][2];
int dp[Maxn + 5][2]; ///< sum[][1] is for all
int flip[Maxn + 5];
inline void update(int u) {
flip[u] = 0;
if (sg[u]) {
if (cnt[u][0] == 1) flip[u] = dp[u][0];
}
else flip[u] = dp[u][1]; // flip a son can change everything
}
void dfs1(int u, int par) {
for (const int& v: g[u]) if (v ^ par)
dfs1(v, u), ++cnt[u][sg[v]];
sg[u] = !!cnt[u][0];
for (const int& v: g[u]) if (v ^ par) {
dp[u][1] += flip[v] + (!sg[v]);
if (!sg[v]) dp[u][0] += flip[v] + 1;
}
update(u);
}
int sum[2], win;
void dfs2(int u, int par) {
int _sg, _fli, _cnt[2], _dp[2];
_sg = sg[u], _fli = flip[u];
memcpy(_cnt, cnt[u], sizeof _cnt);
memcpy(_dp, dp[u], sizeof _dp);
win += _sg, chkadd(sum[_sg], _fli);
if (!_sg) chkadd(sum[_sg], 1);
for (const int& v: g[u]) if (v ^ par) {
--cnt[u][sg[v]];
if (!sg[v]) dp[u][0] -= flip[v] + 1;
dp[u][1] -= flip[v] + (!sg[v]);
sg[u] = !!cnt[u][0];
update(u);
++cnt[v][sg[u]];
if (!sg[u]) dp[v][0] += flip[u] + 1;
dp[v][1] += flip[u] + (!sg[u]);
sg[v] = !!cnt[v][0];
update(v);
dfs2(v, u);
sg[u] = _sg, flip[u] = _fli;
memcpy(cnt[u], _cnt, sizeof _cnt);
memcpy(dp[u], _dp, sizeof _dp);
}
}
matrix acc, a;
signed main() {
// freopen("tree.in", "r", stdin);
// freopen("tree.out", "w", stdout);
input();
dfs1(1, 0);
dfs2(1, 0);
// fprintf(stderr, "sum[0] == %d, sum[1] == %d\n", sum[0], sum[1]);
// fprintf(stderr, "win == %d\n", win);
acc.c[0][0] = (1ll * (n - win) * n % Mod + Mod - sum[0] + sum[1]) % Mod;
acc.c[0][1] = 1ll * (n - win) * n % Mod;
acc.c[1][1] = 1ll * win * n % Mod;
acc.c[1][0] = (1ll * win * n % Mod + Mod - sum[1] + sum[0]) % Mod;
// acc.print();
a.c[0][0] = n - win, a.c[1][0] = win;
a = qkpow(acc, D - 1) * a;
// a.print();
int t0 = a.c[0][0], t1 = a.c[1][0], ans = 0;
if (sg[1])
ans = (1ll * (n - flip[1]) * (t0 + t1) % Mod + 1ll * flip[1] * t1 % Mod) % Mod;
else ans = 1ll * (flip[1] + 1) * t0 % Mod;
writln(ans);
return 0;
}
/**
*
* @p D is too large -> use matrix to accelerate DP transfer
*
* When @p D == 0, tree DP is enough
* sg(u) = \Or_{v is the son of u} (sg(v) ^ 1)
*
* When @p D == 1
* root-changing DP?
* regard the first node we reach on the second tree as the root
* and run the game
*
* pay attention to that "root" is the key
* the only thing that we need to care about is the value of sg() of the root
*
* define f(i, s):
* the number of ways
* to make the root of tree @p i satisfy sg(root) = @p s
*
* a question that some points' changing may affect the state of root
* use root-changing DP to solve it!
*
*/
排序 / Sort
这个题实际上和 <2022-02-26 可恶!然而......:排队 / Queue> 是一样的,因为,存在这样一个等式......
因此,需要满足的结论也应当是一样的,即 \(\max(i-p_i,0)=d(i)\),然后就有个同样的结论:要么前面没有比它大的(\(i<p_i\)),要么比它小的全在它前面(\(i>p_i\)),然后设计 DP 状态,\(f(i,j)\) 表示当前位置是 \(i\),前面的数字集合的最大值为 \(j\) 时的方案数,那么有
同样的前缀和,就有
看似又归约到卡塔兰数的问题上,但是有个问题:如何比较字典序?实际上很简单,用类似数位 DP 的方法做即可,即我们每次贴近一个前缀,然后统计从枚举的这个位置 \(i\) 开始 “出超” 的方案数,因为 “出超” 之后就不受 \(p\) 的限制而只受 \(y=x\) 的限制,因此可以直接用卡塔兰数来算,实际上和 <排队> 是一样的,钦定前缀进行计算。时间复杂度 \(\mathcal O(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 Mod = 998244353;
const int Maxn = 2000000;
inline void chkadd(int& x, int y) { if ((x += y) >= Mod) x -= Mod; }
int fac[Maxn + 5], inv[Maxn + 5], finv[Maxn + 5];
inline void prelude() {
fac[0] = fac[1] = inv[0] = inv[1] = finv[0] = finv[1] = 1;
rep (i, 2, Maxn) {
fac[i] = 1ll * fac[i - 1] * i % Mod;
inv[i] = 1ll * (Mod - Mod / i) * inv[Mod % i] % Mod;
finv[i] = 1ll * finv[i - 1] * inv[i] % Mod;
}
}
inline int C(int n, int m) {
if (n < m || n < 0 || m < 0) return 0;
return 1ll * fac[n] * finv[n - m] % Mod * finv[m] % Mod;
}
inline int cata(int n, int m) {
return (C(n + m, m) + Mod - C(n + m, m - 1)) % Mod;
}
int p[Maxn + 5], n;
bool used[Maxn + 5];
inline void solve() {
readin(n);
rep (i, 1, n) readin(p[i]);
int mex = 1, mx = 0, ans = 0;
memset(used + 1, false, n);
rep (i, 1, n) {
int y = max(mx + 1, p[i] + 1);
used[p[i]] = true;
if (y <= n) chkadd(ans, cata(n - i + 1, n - y));
if (mx < p[i]) mx = p[i];
else if (p[i] != mex) break;
while (used[mex]) ++mex;
}
writln(ans);
}
signed main() {
freopen("sort.in", "r", stdin);
freopen("sort.out", "w", stdout);
prelude();
rep (_, 1, readret(1)) solve();
return 0;
}