闲话 23.3.17

闲话

今日闲话【碎片】
打每日刷新的副本时可能随机掉落,当且仅当今日有模拟赛时掉落概率不为 0。

怎么这几天的闲话阅读量单调递减呢?
没人想看我的垃圾博客;;

模拟赛

口胡万岁!

T1
考虑把空间和基对应,为方便,我们只计算最简阶梯矩阵。同样的,一组基的张成中最大向量就是这组基的加和,这启发我们作数位 dp。
f(i,j,b) 表示填到第 i 位,基大小为 j,卡上界当 [b] 为真。设当前位的上界是 bnd,考虑是否卡界/添加主元可以得到

f(i,j,b)f(i1,j+1,b)(bnd=1)

2j1×f(i,j,b)f(i1,j,b)(j>0)

max(2j1,1)×f(i,j,b)f(i1,j,bbnd=0)

总复杂度 O(log2n)

code
#include <bits/stdc++.h>
using namespace std; 
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
template<typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
template<typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 45;
int n, f[N][N][2];

int main() {
	iot; cin >> n;
    f[30][0][1] = 1;
    pre(i,30,1) rep(j,0,30) rep(k,0,1) if (f[i][j][k]) {
		int top = k ? (n >> (i - 1) & 1) : 1;
		addi(f[i - 1][j][k && 0 == top], (1ll << max(j - 1, 0)) * f[i][j][k] % mod);
		if (top == 1) {
			addi(f[i - 1][j + 1][k], f[i][j][k]);
			if (j > 0) addi(f[i - 1][j][k], (1ll << j - 1) * f[i][j][k] % mod);
		} 
	}
    int ans = 0;
	rep(i,0,30) addi(ans, f[0][i][0], f[0][i][1]);
	cout << ans << '\n';
}

T2
首先有个假做法是点分治并用可撤销并查集拼起来,因为两条链可以拼合。
联通块的充要条件是点集 生成边集 =1,因为是树。
考虑把一条 T2 上路径 uv 映射到二维平面上的一个点 (dfn[u],dfn[v])
考虑 T1 的每个元素对这个平面的贡献。对一条边 (u,v),包含它的所有路径组成了 O(1) 个矩形,因为两个端点的可行区间在 dfn 序上是连续(分段连续)的。对一个点 u 枚举一下出边,能发现有 O() 个矩形。
因此我们就把这 T1 上每个联通块对链的贡献转化成了矩形上的 O(n) 次加减,点加边减。统计平面上值为 1 的点数即可,这可以通过线段树维护最小值和数量,作扫描线得到。
总时间复杂度 O(nlogn)

code
#include <bits/stdc++.h>
using namespace std; 
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
template<typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
template<typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 1e5 + 10;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int n, t1, t2, u[N], v[N]; 
vector<tuple<int, int, int>> vc[N];
ll ans;
vi g[N];

int siz[N], dep[N], dfn[N], stp, st[N][21];
void dfs(int u, int ft) {
    siz[u] = 1, dep[u] = dep[ft] + 1, dfn[u] = ++ stp, st[u][0] = ft;
    rep(j,1,20) st[u][j] = st[st[u][j - 1]][j - 1];
    for (auto v : g[u]) if (v != ft) dfs(v, u), siz[u] += siz[v];
}
inline int getson(int u, int v) {
    pre(j,20,0) if (dfn[st[u][j]] > dfn[v]) u = st[u][j];
    return u;
}

struct sgt {
    #define ls (p << 1)
    #define rs (p << 1 | 1)
    #define mnv(p) seg[p].mnv
    #define cnt(p) seg[p].cnt
    #define lzy(p) seg[p].lzy

    struct node {
        int mnv, cnt, lzy;
    } seg[N << 2];

    inline void ps_p(int p) {
        mnv(p) = min(mnv(ls), mnv(rs)); 
        cnt(p) = cnt(ls) * (mnv(ls) == mnv(p)) + cnt(rs) * (mnv(rs) == mnv(p));
    }
    inline void ps_d(int p) {
        if (!lzy(p)) return;
        mnv(ls) += lzy(p), lzy(ls) += lzy(p);
        mnv(rs) += lzy(p), lzy(rs) += lzy(p);
        lzy(p) = 0;
    }

    void build(int p, int l, int r) {
        mnv(p) = lzy(p) = 0, cnt(p) = 1; 
        if (l == r) return;
        int mid = l + r >> 1;
        build(ls, l, mid), build(rs, mid + 1, r);
        ps_p(p);
    }
    void update(int p, int l, int r, int L, int R, int va) {
        if (L <= l and r <= R) return static_cast<void>(mnv(p) += va, lzy(p) += va);
        ps_d(p); int mid = l + r >> 1;
        if (L <= mid) update(ls, l, mid, L, R, va);
        if (mid < R) update(rs, mid + 1, r, L, R, va);
        ps_p(p);
    }
} Tr;

inline void insert(int l1, int r1, int l2, int r2, int va) {
    if (l1 <= r1 and l2 <= r2) 
        vc[l1].eb(l2, r2, va), vc[r1 + 1].eb(l2, r2, -va);
}

signed main() {
    iot; 
    multi {
        cin >> n;
        rep(i,2,n) cin >> u[i] >> v[i];
        rep(i,2,n) cin >> t1 >> t2, g[t1].eb(t2), g[t2].eb(t1);
        dfs(1, 0);
        rep(u,1,n) {
            for (auto v : g[u]) {
                if (v == st[u][0]) insert(1, dfn[u] - 1, dfn[u], dfn[u] + siz[u] - 1, 1), insert(dfn[u] + siz[u], n, dfn[u], dfn[u] + siz[u] - 1, 1);
                else insert(dfn[v], dfn[v] + siz[v] - 1, 1, dfn[v] - 1, 1), insert(dfn[v], dfn[v] + siz[v] - 1, dfn[v] + siz[v], n, 1);
            } insert(dfn[u], dfn[u], 1, n, 1);
        }
        rep(i,2,n) {
            if (dfn[u[i]] < dfn[v[i]]) swap(u[i], v[i]);
            if (dfn[u[i]] < dfn[v[i]] + siz[v[i]]) {
                int t = getson(u[i], v[i]);
                insert(dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, 1, dfn[t] - 1, -1), insert(dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, dfn[t] + siz[t], n, -1);
                insert(1, dfn[t] - 1, dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, -1), insert(dfn[t] + siz[t], n, dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, -1);
            } else {
                insert(dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, dfn[v[i]], dfn[v[i]] + siz[v[i]] - 1, -1);
                insert(dfn[v[i]], dfn[v[i]] + siz[v[i]] - 1, dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, -1);
            }
        }
        Tr.build(1, 1, n);
        rep(i,1,n) {
            for (auto [l, r, va] : vc[i]) Tr.update(1, 1, n, l, r, va);
            // cout << Tr.seg[1].mnv << ' ' << Tr.seg[1].cnt << '\n';
            if (Tr.seg[1].mnv == 1) ans += Tr.seg[1].cnt;
        }
        cout << (ans + n) / 2 << '\n';
        rep(i,1,n) g[i].clear(), vc[i].clear(); ans = stp = 0;
    }
}

T3
考虑确定一个询问子串的情况。我们可以首先找到所有出现位置 S={[li,ri]},随后可以贪心地算出最少加入数。具体地,我们每次找最左边的区间,在他右端点前放一个间隔符,并删除所有包含这间隔符的区间即可。这个上线段树是 O(|S|logn) 的,用 radix sort + umap 是 O(|S|) 的。
第二种做法具体如下:
设当前拿到的区间集合是 S={[li,ri]},这是左值+右值各 |S| 个。首先把 2|S| 个值 radix sort,从小到大扫值域。如果当前值是左值,把对应区间加入一个 umap。otherwise 给 umap 里所有值打标记并清空 umap,若 umap 清空前不空就答案加 1;如果这个右值对应的左值被打了标记就不清空,略过即可。容易发现这做法是 O(|S|) 的。
我们自然想到了根号分治。
对于 B 的询问,我们发现可能的总串数是 O(nB) 的,可以 O(nB) 地预处理。具体地,我们对每个 B 的长度 k,找到 nk 个可能的串,每次拿出所有相等的串作如上的贪心。这样总复杂度是 O(nB)O(1) 的。
对于 B 的询问,如果暴力按上面做法做很可能劣化到单次 O(n)。但是从上面的想法我们能看出一个性质:若长度 B,清空 umap 的次数不超过 n/B。这告诉我们,上线段树的总时间复杂度是 O(nlogn/B) 的。这样我们就只需要快速找到所有右端点。
不难发现这右端点本质上是询问串的 endpos,因此得到原串的后缀链接树后即可在 O(nlogn) 的复杂度内通过 SAM 上线段树合并的方式得到每个询问串的 endpos 集合。随后在 SAM 上查出串,用线段树做贪心即可。
q=O(n),总时间复杂度为 O(nB+nlogn/B),取 O(nlogn) 得到时间复杂度 O(nnlogn)

code
#include <bits/stdc++.h>
using namespace std; 
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
template<typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
template<typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 2e5 + 5, B = 450;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int n, m, sqr, ret[N][B];
char S[N];
vi vec[N], g[N];

struct sgt {
	#define ls(p) seg[p].l
	#define rs(p) seg[p].r

	int mlc, rt[N];
	struct node { 
		int l, r; 
	} seg[N << 6]; 

	void update(int& p, int l, int r, int x) {
		if (!p) p = ++ mlc;
		if (l == r) return;
		int mid = l + r >> 1;
		if (x <= mid) update(ls(p), l, mid, x);
		else update(rs(p), mid + 1, r, x);
	} 
	
	int merge(int x, int y) {
		if (!x or !y) return x | y;
		int ret = ++mlc;
		ls(ret) = merge(ls(x), ls(y));
		rs(ret) = merge(rs(x), rs(y));
		return ret;
	}

	int query(int p, int l, int r, int x) {
		if (!p) return 0;
		if (l == r) return l;
		int mid = l + r >> 1;
		if (x > mid) return query(rs(p), mid + 1, r, x);
		int ret = query(ls(p), l, mid, x);
		if (ret) return ret;
		return query(rs(p), mid + 1, r, x);
	}
} Tr;

int son[N][26], link[N], len[N], pos[N], mlc = 1, lst = 1;
void extend(int c, int id) {
	int now = ++ mlc, p = lst;
	len[now] = len[lst] + 1;
	while (p and !son[p][c]) 
		son[p][c] = now, p = link[p];
	if (p == 0) link[now] = 1;
	else {
		int q = son[p][c];
		if (len[p] + 1 == len[q]) link[now] = q;
		else {
			int kage = ++ mlc;
			len[kage] = len[p] + 1, link[kage] = link[q];
			memcpy(son[kage], son[q], sizeof(son[q]));
			while (p and son[p][c] == q) 
				son[p][c] = kage, p = link[p];
			link[q] = link[now] = kage;
		}
	} 
	pos[id] = lst = now;
}

int fa[N][21];
void dfs(int u) {
	rep(i,1,20) fa[u][i] = fa[fa[u][i - 1]][i - 1];
	for(int v : g[u]) {
		dfs(v);
		if (len[u] > sqr) Tr.rt[u] = Tr.merge(Tr.rt[u], Tr.rt[v]);
	}
}
int find(int u, int d) {
	pre(i,20,0) if (len[ fa[u][i] ] >= d) u = fa[u][i]; return u;
}

signed main () {
	iot; 
	cin >> n >> m >> S + 1;
	sqr = sqrt(n * log(n));
	rep(i,1,n) extend(S[i] - 'a', i);
	rep(i,1,mlc) fa[i][0] = link[i], g[link[i]].eb(i);
	rep(i,1,n) Tr.update(Tr.rt[pos[i]], 1, n, i);
	rep(i,1,n) for(int u = find(pos[i], sqr); u; u = link[u]) vec[u].eb(i);
	dfs(1);
	rep(i,1,mlc) rep(d,len[link[i]] + 1,min(len[i],sqr)) {
		int p = 1;
		for(auto it : vec[i]) if (it - d + 1 >= p) ++ret[i][d], p = it;
	}

	rep(i,1,m) {
		int l, r;
		cin >> l >> r;
		if (l == r)  { cout << -1 << "\n"; continue; }
		int d = r - l + 1, u = find(pos[r], d);
		if (d <= sqr) cout << ret[u][d] << "\n";
		else {
			int p = 1, ret = 0;
			while(p + d - 1 <= n and (p = Tr.query(Tr.rt[u], 1, n, p + d - 1))) ++ ret; 
			cout << ret << "\n";
		}
	}
}

杂题

CF1278F

m 张牌,其中一张是王牌。现在你执行 n 次如下操作:洗牌后查看第一张牌是什么。

x 为洗牌后第一张牌为王牌的次数,现在假设洗牌时 m! 种牌的排列出现的概率均相等,求 xk 的期望值。

1n,m998244532,1k105

考虑出现王牌的概率是 m1,我们可以简单地枚举出现王牌的次数以及对答案的贡献。答案就是

i0(ni)ikmi(1m1)ni= 1mni0(ni)ik(m1)ni= 1mni0j=0kj!(ni)(ij){kj}(m1)ni= 1mni0j=0kj!(nj)(njij){kj}(m1)ni= 1mni0j=0kj!(nj)(njij){kj}(m1)ni= 1mnj=0kj!(nj){kj}i0(njni)(m1)ni= 1mnj=0kj!(nj){kj}mnj= j=0k{kj}nj_mj

可以 O(klogk) 地求得。

Submission.



AGC040F

有两个棋子初始点都在坐标 0,两个棋子之间没有区别,总共要进行 n 次操作,每次操作是如下操作其中之一:

  1. 选择一个棋子向前移动一步。

  2. 选择位置较后的那个棋子,直接移动到位置较前的那个棋子的位置。

计数 n 次操作后两个棋子分别在位置 a,b 的方案,对 998244353 取模。两种方案是相同的,当且仅当两个棋子在每一步的坐标都是相同的(注意不是每一步的操作都相同)。

1abn107

又到了今天的生成函数时间!
什么?你看这题面不像?我也看着不像。

但是确实可以。
约定一个高维生成函数 F(x1,,xk) 的第 x1xk 项记为 F[x1,,xk]
欸,你别害怕啊!

你看 2. 操作不是很好刻画。那就不刻画了,先找性质。
可以发现虽然 2. 操作执行次数不定,但是其对两个棋子的影响是一样的,这也就让我们可以先只考虑 1. 操作(可以为空),再考虑加一个 2 操作的影响,用 SEQ 构造拼起来。

现在只讨论 1. 操作。首先扔到二维平面上,然后就是只能向右向上走,问有多少种走法从 (0,0)(x,y) s.t. x<y 且不经过 y=x;这是由于 (x,y) 在施 2. 操作后都可以到达 (y,y),本质相同。

转二维平面路径为括号序列。
我们可以发现,如果设 O 为合法括号匹配,答案一定形如 O(O(...O(O,这启发我们首先计数 (O,再拼出答案。设卡特兰数的 ogf 是 C(z),并让 u 表示左括号的占位元,v 表示右括号的占位元,则形如 (O 的括号序列的组合类 O1 就能表为 v(1+iC[i]uivi)=v(1+C(uv))
设只讨论 1. 操作的操作序列的组合类是 G,则 G=SEQ(O1),也就是

G(u,v)=11v(1+C(uv))

我们要算答案,首先要用 u,v 分别表示两个棋子的坐标,还要加一维 w 表示用的次数。设答案的组合类是 F,ogf 是 F(u,v,w)
可以发现,若记一系列极长的 1. 操作为 C,一次 2. 操作为 S,则最终操作序列一定形如 CSCS...CSC,并且最后一定停止在 (a,b)。我们不妨考虑先构造 CS,再加入一个 C。为方便,记他们的组合类是 CS,C
CS 就考虑一次 C 到定点 (a,b),再冲到 (b,b) 的贡献。这过程的方案数是 G[a,b],对 u,v 的贡献都是 b,对 w 的贡献是 a+b+1。也就是

(a,b)G[a,b]ubvbwa+b+1=w(a,b)G[a,b]wa(uvw)b=wG(w,uvw)

C 的话类似,我们只需要考虑前半部分的贡献。这过程的方案数是 G[a,b],对 u 的贡献是 av 的贡献是 b,对 w 的贡献是 a+b。也就是

(a,b)G[a,b]uavbwa+b=(a,b)G[a,b](uw)a(vw)b=G(uw,vw)

我们知道 F=SET(CS)×C,也就是

F(u,v,w)=G(uw,vw)1wG(w,uvw)

答案就是 [uavbwn]F

考虑分母 G 的第二维传进去了 uvw,我们不妨先把他干掉。设 x=uv,y=v,我们要的就是

[uavbwn]G(uw,vw)1wG(w,uvw)= [xaybawn]G(xw/y,yw)1wG(w,xw)= [xawn]11wG(w,xw)[yba]G(xw/y,yw)= [xawn]11wG(w,xw)[yba]11yw(1+C(xw2))= [xawn]11wG(w,xw)[yba]k0(w(1+C(xw2)))iyi= [xawn]wba(1+C(xw2))ba1wG(w,xw)= [xawn+ab](1+C(xw2))ba1w(1xw(1+C(xw2)))1= [xawn+ab](1+C(xw2))ba(1xw(1+C(xw2)))1xw(1+C(xw2))w

到这一步,考虑对 k,有

[xa]xkwk(1+C(xw2))ba+k=w2ak[xa]xk(1+C(x))ba+k

我们知道,无论我们要的系数是什么,(1+C(xw2))ba+k 都是一个多项式,因此我们想提取的 xak 项一定形如 fk(xw2)ak。所以我们只需要提取出 w2(ak),之后对提取 xak 项系数来说这多项式就等价于 (1+C(x))ba+k 了。这自然能证明如上的公式。

上面的结论想用到分式上还得讨论一下。上下两个部分得到的都是一个 xwf(xw2) 的形式,我们需要提取 xa 项的 w,也就需要总共提取一个 w2a,并在 x 前留下一个 w2。具体看如下的推导:

= [xawn+ab](1+C(xw2))ba(1xw(1+C(xw2)))1xw(1+C(xw2))w= [wn+ab]w2a[xa](1+C(x))ba(1xw12(1+C(x)))1xw12(1+C(x))w= [xawnab](1+C(x))ba(1x(1+C(x)))/w1x(1+C(x))/ww

不妨设置哑元 t,令 tk=[xa]xk(1+C(x))ba+k,显然有 t0=[xa](1+C(x))ba=1。有

= [xawnab](1+C(x))ba(1x(1+C(x)))/w1x(1+C(x))/ww= [wnab]1t/w1t/ww= [wnab](1t/w)i0(t/w+w)i= [wnab](1t/w)i0k0(i+ki)tiwki= i0(nab+2ii)tii0(nab+1+2ii)ti+1

最后的问题是如何算 tk,以及顺便验证 t0=1
考虑 t 是一个存在复合逆的函数的复合方程的形式,直觉告诉我们可以通过另类拉格朗日反演得到简洁的形式。

tk=[xa]xk(1+C(x))ba+k=[xa](xk(1+x)2)k(1+x)ba+k1x(1+x)3(1+x)2(a+1)=[xak](1x)(1+x)a+bk1=(a+bk1ak)(a+bk1ak1)

到这里就完全解决了本题。注意 ta+b=[b=0]

总时间复杂度 O(n)

Submission.

posted @   joke3579  阅读(127)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
点击右上角即可分享
微信分享提示