闲话 23.3.27

闲话

某人写了 INTERNET YAMERO 上黑板
……我觉得会被跳过
另一个人写了 Brain Power 上黑板
A 老师:这玩意有词?

美式咖啡喝凉的好还是热的好?
你说得对,但是我喝摩卡和拿铁

模拟赛

这模拟赛打的 明天是要模拟如何翻盘?

T1
签到题。考虑直接枚举前面的 BABA...BA[1,r],这样的 r 可以枚举 BA 长度后 check O(n) 段得到。然后就是统计 [r+1,n] 段和 BA 的 lcp 长度,可以二分得到。
通过哈希实现的总时间复杂度 O(nlogn),注意数组是否越界。

T2
考场降智时刻。
考虑第一问。设 f(u) 表示子树内叶子情况全知道的最小花费,g(u) 表示有且只有一个叶子不知道的最小花费,转移就是 g(u)=minw{vwf(v)+g(w)}, f(u)=min{vf(v),g(u)+c[u]}。叶子初值 f(u)=c[u],g(u)=0
考虑第三问。设 fw(u) 表示子树内叶子情况全知道的所有情况,gw(u) 表示有且只有一个叶子不知道的所有情况,我们只需要按上面的最小情况转移即可。也就是说,上面加法改成乘法,上面最小值改成把所有最小情况加和即可。叶子初值 fw(u)=gw(u)=1
考虑第二问。我们按照上面的情况直接再做一遍递归即可。也就是说,我们按照最优方案 dfs,dfs 时记录当前节点子树内还有几个叶子不知道,再记忆化,保证只搜索 O(n) 次。
总时间复杂度 O(n)

T3
f(u,h) 表示到 u 点,还剩 h 点生命值时答案的期望。不难写出

f(u,h)=min{Hh+f(1,H),1+1deg[u]uvf(v,hdv)}

我们发现,这是有后效性的,但是所有后效性只取决于 f(1,H) 这一个点的值。考虑如何把这个点的值删去。
引入变元 x=f(1,H),我们设 f(u,h)(x) 为如上的期望对变元的函数。可以证明的是,i,h, ddxf(u,h)(x)1
证明考虑归纳法。
我们首先观察 f(n,h)(x),这些函数总是定值 0,因此 ddxf(u,h)(x)=01
我们已经知道了对 uv, f(v,h)(x) 全满足这条件,现在需要证明 f(u,h)(x) 也满足这条件。可以简记形态为

f(x)=min{x+c,1ki=1kgi(x)+c}

其中 gi(x), ddxgi(x)1。它的曲线非光滑,但由于间断点都是第一类的,我们可以只对连续的部分考虑导数,最后讨论间断点。也就是说我们只需要对 min 的前后两部分分别归纳证明。前一部分是显然的,\dfrac{\text d}{\text dx} (x + c) = 1 \le 1$,而后一部分可以知道

ddx(1ki=1kgi(x)+c)=1ki=1kddxgi(x)1ki=1k1=1

由于是取 min 操作,因此图像连续。由于函数在光滑部分的导数 1,并由 min 操作的性质,任意两个间断点总是由导数大的函数切换至导数小的函数。所以 f(u,h)(x) 的导数小于等于 min 的两个参数的。
这也就证明了 f(u,h)(x) 也满足这条件。
考虑这是个 dag,点 u 的出边只会指向 >u 的点。因此假设我们已知了 [l,n] 段都符合要求,总存在一个只连向 [l,n] 段的节点使得我们可以拓展符合要求的区间,取 l1 即可。
证毕。
所以我们也能知道 ddxf(1,H)(x)1,即 ddx(f(1,H)(x)x)0。这也就说明了 f(1,H)(x)x 是有单调性的。我们只需要二分得到 f(1,H)(x)x=0x,这 x 就是答案。
总时间复杂度 O(nHlogV),其中 V 是答案的值域。

杂题

CF1747E

给定 nm,请问能构造满足以下条件的数组对 (a,b) 中,a 长度之和是多少:

  1. 数组长度相等且长度大于 2,设数组长度都为 k
  2. a1=b1=0ak=nbk=m
  3. 每个数组都单调不下降且对于所有 1<ik 都满足 ai+biai1+bi1

答案对 109+7 取模。

1n,m5×106

好像有高妙格路计数,不是很懂。

考虑数组单调不降,所以 ai+biai1+bi1,等号当且仅当 ai=ai1,bi=bi1。所以 3. 条件说的很明白了:两个数组不升的位置不能有重合。
这启发我们枚举不升的位置计数。我们枚举长度是 k+1a,b 序列升的位置分别为 i,j 个。首先有 (k+1) 的贡献,并且分配 n/m 的值给 i/j 个升的位置等价于 n/m 个球放在 i/j 个有标号盒子里且不能有空盒子,方案数就是 (n1i1)/(m1j1)。最后是分配不升的位置,首先分配 a 序列的方案数是 (ki) 个,而 b 序列可以考虑 kj 个不升的位置必须要和 ia 中升的位置重合,方案数是 (jki)。所以有答案就是

k1ij(k+1)(n1i1)(m1j1)(ki)(ikj)= k1i(k+1)(n1i1)(ki)j(m1j1)(ikj)= k1i(k+1)(n1i1)(ki)(m+i1k1)= k1i(k+1)(n1i1)ki(k1i1)(m+i1k1)= k1i1ik(k+1)(n1i1)(m+i1k1)(k1i1)= k1i1ik(k+1)(n1i1)(m+i1i1)(mki)= i01i(n1i1)(m+i1i1)k1k(k+1)(mki)= i01i(n1i1)(m+i1i1)k0(k+i)(k+i+1)(mk)

接下来我们要解决的是 k0(k+i)(k+i+1)(mk) 的计算。我们不妨考虑更广泛的情况,即已知一个 d 次多项式 F(k) 的系数,计算 k0F(k)(mk)
由于这题 d 较小,我们可以考虑直接计算每个 k0kv(mk)你定睛一看——这不是《载谭 Binomial Sum》吗?所以可以做到 O(k + log n)
由于这题 d=2,我们可以直接考虑计算每个 v=0/1/2
v=0 时是经典结论 2m
v=1 时可以知道答案就是

k0k(mk)+k0(mk)(mk)2=m2k0(mk)=m2m1

v=2 时可以拆成下降幂形式,自然得到。

所以我们可以 O(n) 解决原问题。



P1117

如果一个字符串可以被拆分为 AABB 的形式,其中 AB 是任意非空字符串,则我们称该字符串的这种拆分是优秀的。现在给出一个长度为 n 的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。

1n105

好久之前就想学这个套路了但是一直没写(

假设从 i 点开始的形如 AA 的串数是 a(i),从 i 点结束的形如 AA 的串数是 b(i),则 i=1n1a(i)×b(i+1) 就是答案。考虑如何计算这两个值。

考虑枚举长度 len,我们每次统计长度为 len 的串会对哪些点的 a,b 产生贡献。
我们在 len,2len, 的位置撒关键点,这样每个形如 AA 的串都会经过且仅经过相邻的 2 个点。由于总点数是 O(nlogn) 的,我们可以枚举相邻点算贡献。假设当前的两个点是 l=kr=k+len

假设我们求出了前缀 [1,l][1,r] 的最长公共后缀 S,以及后缀 [l+1,n][r+1,n] 的最长公共前缀 T
考虑当 |S|+|T|<len 时不可能有解,我们总能取 [l+|T|,r|S|] 中一个字符作为两个串中不等的字符的例子。
而当 |S|+|T|len 时,我们总能从 l|S|+1 位置开始向后取一段,这一段内都可以作为 AA 串的起点,直到最右侧达到 r+|T|1。终点类似。

这样我们就可以对 a,b 做区间加了。由于我们是先加再求,考虑差分维护即可。最长公共前后缀可以采用 SA + st表的方式求解。
总时间复杂度 O(nlogn)

类似题:UVA10829

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_; for (int TestNo = 1; TestNo <= _T_; ++ TestNo)
#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 = 30000 + 10;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int n, a[N], b[N];
template <typename _Tp = char, const int siz = 127>
struct SA {
	_Tp ch[N];
	int n = -1, sa[N], rk[N], heg[N];
	int a[N], tr[siz + 5], cur[N];
	int sum[N], bse[N << 3], *_t = bse;

	_Tp* begin() { return ch + 1; }
	const _Tp operator [] (const int & p) const { return ch[p]; }
	_Tp & operator [] (const int & p) { return ch[p]; }

	#define _MemAlloc(pool, size, tag) (tag = pool, pool += size)
	#define pushs(x) (sa[cur[a[x]] --] = x)
	#define pushi(x) (sa[cur[a[x]] ++] = x)
	#define inds(lms)  		\
		rep(i,1,n) sa[i] = -1, sum[i] = 0; \
		rep(i,1,n) sum[a[i]] ++; \
		rep(i,1,n) sum[i] += sum[i - 1]; \
		rep(i,1,n) cur[i] = sum[i]; \
		pre(i,m,1) pushs(lms[i]); \
		rep(i,1,n) cur[i] = sum[i - 1] + 1; \
		rep(i,1,n) if (sa[i] > 1 and !tp[sa[i] - 1]) pushi(sa[i] - 1); \
		rep(i,1,n) cur[i] = sum[i]; \
		pre(i,n,1) if (sa[i] > 1 and tp[sa[i] - 1]) pushs(sa[i] - 1);

	inline void SA_IS(int n, int* a) {
		int* tp; _MemAlloc(_t, n + 1, tp); tp[n] = 1;
		int* p; _MemAlloc(_t, n + 2, p);
		pre(i,n-1,1) tp[i] = (a[i] == a[i + 1]) ? tp[i + 1] : (a[i] < a[i + 1]);
		int m = 0, tot = 0;
		rep(i,1,n) rk[i] = (tp[i] and !tp[i - 1]) ? (p[++ m] = i, m) : -1;
		inds(p);
		int* a1; _MemAlloc(_t, m + 1, a1);
		p[m + 1] = n;
		for (int i = 1, x, y; i <= n; ++ i) if ((x = rk[sa[i]]) != -1) {
			if (tot == 0 or p[x + 1] - p[x] != p[y + 1] - p[y]) ++ tot;
			else for (int p1 = p[x], p2 = p[y]; p2 <= p[y + 1]; ++ p1, ++ p2)
				if ((a[p1] << 1 | tp[p1]) != (a[p2] << 1 | tp[p2])) { ++ tot; break; }
			a1[y = x] = tot;
		}
		if (tot == m) rep(i,1,m) sa[a1[i]] = i;
		else SA_IS(m, a1);
		rep(i,1,m) a1[i] = p[sa[i]];
		inds(a1);
	}

	int st[N][20], lgv[N];
	void build() {
		n = strlen(begin());

		memset(tr, 0, sizeof tr);
		rep(i,1,n) tr[ch[i]] = 1;
		rep(i,1,siz + 1) tr[i] += tr[i - 1];
		rep(i,1,n) a[i] = tr[ch[i]] + 1;
		a[n + 1] = 1; _t = bse;
		SA_IS(n + 1, a); 
		rep(i,1,n) sa[i] = sa[i + 1];
		rep(i,1,n) rk[sa[i]] = i;
		
		for (int i = 1, k = 0; i <= n; ++ i) {
			if (rk[i] == 0) continue;
			if (k) -- k;
			while (ch[i + k] == ch[sa[rk[i] - 1] + k]) ++ k;
			heg[rk[i]] = k;
		}

		rep(i,1,n) st[i][0] = heg[i];
		rep(i,2,n) lgv[i] = lgv[i >> 1] + 1;
		rep(i,1,lgv[n]) for (int j = 1; j + (1 << i) - 1 <= n; ++ j) 
			st[j][i] = min(st[j][i - 1], st[j + (1 << i - 1)][i - 1]);
	}

	int lcp(int l, int r) {
		if (l <= 0 or l > n or r <= 0 or r > n) return 0;
		l = rk[l], r = rk[r];
		if(l > r) swap(l, r); l++; 
		int k = lgv[r - l + 1]; 
		return min(st[l][k], st[r - (1 << k) + 1][k]); 
	}
}; SA<> sa[2];

signed main() {
	multi {
		cin >> sa[0].begin();
		sa[0].build(); n = sa[0].n;
		rep(i,1,n) sa[1][i] = sa[0][n - i + 1]; sa[1][n + 1] = 0;
		sa[1].build();
		rep(i,1,n) a[i] = b[i] = 0;
		for (int len = 1; len <= n / 2; ++ len) {
			for (int i = len; i <= n; i += len) {
				int l1 = i, r1 = i + len, l2 = n - (r1 - 1) + 1, r2 = n - (l1 - 1) + 1;
				int lcp = min(len, sa[0].lcp(l1, r1)); 
				int lcs = min(len - 1, sa[1].lcp(l2, r2)); 
				if (lcp + lcs >= len) {
					b[i - lcs] ++, b[i - lcs + (lcp + lcs - len + 1)] --;
					a[r1 + lcp - (lcp + lcs - len + 1)] ++, a[r1 + lcp] --;
				}
			}
		} rep(i,1,n) a[i] += a[i - 1], b[i] += b[i - 1];
		ll ans = 0; 
		rep(i,1,n - 1) ans += 1ll * a[i] * b[i + 1];
		cout << ans << '\n';
	}
} 
posted @   joke3579  阅读(95)  评论(5编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示