jzoj6748 回文串(字符串结论计数,反演, min25)

86 篇文章 0 订阅
12 篇文章 1 订阅

题面

求长度不超过n的双回文串个数。
n ≤ 1 0 9 n\leq 10^9 n109

分析

这是个结论题,主要难点是分析出弱回文串的结构。(或者打表发现他)

弱双回文串=第一个回文串可为空的双回文串。
最小弱回文指的是,最小整周期为n的长度为n的弱回文串。

  1. 一个有多个划分方法的弱双回文串=某个最小弱双回文串的若干复制
  2. 弱双回文串的划分方案数即为,最大的可能复制次数。
  3. 双回文串个数=弱双回文串-最小回文串

对拍,上述结论均正确,开始你的数论表演:
设:
f ( n ) f(n) f(n)为长度为n的最小弱双回文串个数,
g ( n ) g(n) g(n)为长度为n的弱双回文划分个数,
h ( n ) h(n) h(n)为长度为n的弱双回文个数。

g ( n ) g(n) g(n)只需分类讨论一下奇偶,可得
g ( n ) = { n C ( n + 1 ) / 2 , n % 2 = 1 n / 2 ( C n / 2 + C n / 2 + 1 ) , n % 2 = 0 g(n)=\begin{cases} nC^{(n+1)/2},n\%2=1\\n/2(C^{n/2}+C^{n/2+1}),n\%2=0 \end{cases} g(n)={nC(n+1)/2,n%2=1n/2(Cn/2+Cn/2+1),n%2=0

由上述, g ( n ) = ∑ d ∣ n f ( d ) × ( n / d ) g(n)=\sum_{d|n}f(d)\times (n/d) g(n)=dnf(d)×(n/d),反演得 f ( n ) = ∑ d ∣ n g ( d ) ( n / d ) μ ( n / d ) f(n)=\sum_{d|n}g(d)(n/d)\mu(n/d) f(n)=dng(d)(n/d)μ(n/d)
并且有 h ( n ) = ∑ d ∣ n f ( d ) h(n)=\sum_{d|n}f(d) h(n)=dnf(d)
接下来是求解 ∑ i = 1 n h ( n ) \sum_{i=1}^{n}h(n) i=1nh(n)
= ∑ i = 1 n ∑ d ∣ i f ( d ) =\sum_{i=1}^{n}\sum_{d|i}f(d) =i=1ndif(d)
= ∑ d = 1 n f ( d ) ( n / d ) =\sum_{d=1}^{n}f(d)(n/d) =d=1nf(d)(n/d)
= ∑ d = 1 n ( n / d ) ∑ k ∣ d g ( k ) ( d / k ) μ ( d / k ) =\sum_{d=1}^{n}(n/d)\sum_{k|d}g(k)(d/k)\mu(d/k) =d=1n(n/d)kdg(k)(d/k)μ(d/k)
= ∑ k = 1 n g ( k ) ∑ d ′ = 1 n / k ( n / k d ′ ) μ ( d ′ ) d ′ =\sum_{k=1}^{n}g(k)\sum_{d'=1}^{n/k}(\frac {n/k} {d'})\mu(d')d' =k=1ng(k)d=1n/k(dn/k)μ(d)d
该式可以分块, g g g是个等差比可以快速求和。现在要快速算后面的关于n/k的函数。
T ( n ) = ∑ i = 1 n ( n i ) μ ( i ) i T(n)=\sum_{i=1}^{n}(\frac {n} {i})\mu(i)i T(n)=i=1n(in)μ(i)i,注意括号里是下取整。
= ∑ j = 1 n ∑ d ∣ j μ ( d ) d =\sum_{j=1}^{n}\sum_{d|j}\mu(d)d =j=1ndjμ(d)d,这是个积性函数前缀和,直接min25即可。(也可以杜教筛)

接下来计算最小回文数,基本同理:
b ( n ) b(n) b(n)为长度为n的回文数, c ( n ) c(n) c(n)为长度为n的最小回文。注意到 b ( n ) b(n) b(n)很好算。
b ( n ) = ∑ d ∣ n c ( d ) b(n)=\sum_{d|n}c(d) b(n)=dnc(d)
反演, c ( n ) = ∑ d ∣ n b ( d ) μ ( n / d ) c(n)=\sum_{d|n}b(d)\mu(n/d) c(n)=dnb(d)μ(n/d)
c ( n ) c(n) c(n)求前缀和 ∑ i = 1 n c ( i ) \sum_{i=1}^{n} c(i) i=1nc(i)
= ∑ i = 1 n ∑ d ∣ i b ( d ) μ ( i / d ) =\sum_{i=1}^{n} \sum_{d|i}b(d)\mu(i/d) =i=1ndib(d)μ(i/d)
= ∑ d = 1 n b ( d ) ∑ i = 1 n / d μ ( i ) =\sum_{d=1}^n b(d)\sum_{i=1}^{n/d}\mu(i) =d=1nb(d)i=1n/dμ(i)
分块+min25/杜教筛即可。

结论的证明

(由于我的脑抽,以下弱回文=弱双回文
接下来是对结论的证明,学艺不精可能有伪证。。。请各位抱着批判的眼光看待。。。
首先有弱周期引理:若有周期 p , q p,q p,q p + q ≤ ∣ S ∣ p+q\leq |S| p+qS,则 g c d ( p , q ) gcd(p,q) gcd(p,q)为S的一个周期。

Lemma 2

一个有两个以上划分的弱回文是整周期串。
只需找出两个周期 a , b a,b a,b使得 g c d ( a , b ) ∣ s gcd(a,b)|s gcd(a,b)s。设两个划分的第一个回文长度差值为 t t t,容易发现s同时有 t t t的周期和border.
t的周期:大回文前缀包含小回文前缀,会产生长度为他们的差的周期。
t的border:通过一些回文翻转即可发现。

Lemma 3

弱回文串 S S S的最小整周期是最小弱回文串,并且该串的弱回文划分数恰好为 ∣ S ∣ / 最 小 整 周 期 |S|/最小整周期 S/

设某个弱回文串的最小整周期所对应的串为 T T T,由 L e m m a 2 Lemma 2 Lemma2,T至多只有一个弱回文划分。
下面证明 T T T存在一个弱回文划分。
T = S T=S T=S时引理成立,否则设串 S = p q S=pq S=pq为其弱回文划分,取包含至少一个T的部分,不失一般性地我们取 q q q q q q是若干个T与一个T的可空前缀拼接而成。由于q是回文串,容易发现该前缀是回文串。并且去掉该前缀之后剩下的 T ′ T' T也是回文串。这就证明了 T T T是一个最小弱回文串(只存在唯一划分)。

然后考虑其弱回文划分数。首先至少存在 ∣ S ∣ / ∣ T ∣ |S|/|T| S/T个。然后,由上述分析我们回文划分的分界点必须取在 T T T的回文划分分界点上,否则会与 T T T只存在唯一划分矛盾。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mo = 1e9 + 7, N = 1e6 + 10;

int n, c;
int is[N], mu[N];
ll p[N];
int loc[N], loc2[N], w[N], B, precnt[N], presum[N], preT[N];
#define id(x) ((x) <= B ? loc[x] : loc2[n / (x)])
void init(int n) {
	mu[1] = 1;
	for(int i = 2; i <= n; i++) {
		if (!is[i]) p[++*p] = i, mu[i] = -1;
		for(int j = 1; p[j] * i <= n; j++) {
			is[p[j] * i] = 1;
			if (i % p[j] == 0) {
				break;
			} else {
				mu[i * p[j]] = -mu[i];
			}
		}
		precnt[i] = precnt[i - 1] + (is[i] == 0 ? 1 : 0);
		presum[i] = (presum[i - 1] + (is[i] == 0 ? i : 0)) % mo;
		preT[i] = (preT[i - 1] + (is[i] == 0 ? 1 - i : 0)) % mo;
	}
}

ll cnt[N], sum[N], smu[N], aT[N];



void min25() {
	B = sqrt(n);
	for(int i = 1; i <= n; ) {
		int v = n / i, r = n / v;
		w[++(*w)] = n / i;
		if (v <= B) loc[v] = *w; else loc2[i] = *w;
		cnt[*w] = v - 1;
		sum[*w] = (2ll + v) * (v - 1) / 2 % mo;
		i = r + 1;
	}
	for(int i = 1; p[i] * p[i] <= n; i++) {
		for(int j = 1; w[j] >= p[i] * p[i]; j++) {
			cnt[j] -= cnt[id(w[j] / p[i])] - precnt[p[i] - 1];
			sum[j] = (sum[j] - p[i] * (sum[id(w[j] / p[i])] - presum[p[i] - 1])) % mo;
		}
	}
	
	for(int i = 1; i <= *w; i++) {
		smu[i] = -cnt[i];
		aT[i] = cnt[i] - sum[i];
	}
	
	for(int i = p[0]; i; i--) {
		for(int j = 1; w[j] >= p[i] * p[i]; j++) {
			for(int e = 1, b = p[i]; b * p[i] <= w[j]; e++, b *= p[i]) {
				if (e == 1)
					smu[j] = (smu[j] + (-1) * (smu[id(w[j] / b)] - (-1) * precnt[p[i]]));
				aT[j] = (aT[j] + (1 - p[i]) * (aT[id(w[j] / b)] - preT[p[i]]) + (1 - p[i])) % mo;
			}
		}
	}
}

ll ans;

ll ksm(ll x, ll y) {
	ll ret = 1; for(; y; y >>= 1) {
		if (y & 1) ret = ret * x % mo;
		x = x * x % mo;
	}
	return ret;
}

ll g(ll n) {
	if (n & 1) return n * ksm(c, (n + 1) / 2);
	else return n / 2 * (ksm(c, n >> 1) + ksm(c, (n >> 1) + 1)) % mo;
}

ll A1, B1, A2, B2;
ll _sumg(ll n) {
	ll p1 = ksm(c, n / 2 + 1) * (A1 * (n / 2) % mo + B1) - c * B1;//odd
	p1 %= mo;
	ll dt = 0;
	
	ll p2 = ksm(c, n / 2 + 1) * (A2 * (n / 2) % mo + B2) - c * B2; //even1
	p2 %= mo;
	
	p2 = p2 * (1 + c) % mo; //even2
	return (p1 + p2) % mo;
}

ll sumg(ll n) {
	ll rv = _sumg(n - (n & 1)) + ((n & 1) ? g(n) : 0);
	return rv % mo;
}

ll b(ll n) {
	return ksm(c, (n + 1) >> 1);
}

ll _sumb(ll n) {
	ll cnt = n >> 1;
	return 2 * (ksm(c, cnt + 1) - c) % mo * ksm(c - 1, mo - 2) % mo;
}

ll sumb(ll n) {
	//ll ret = 0;
	ll rv = _sumb(n - (n & 1)) + (n & 1 ? b(n) : 0);
	//for(int i = 1; i <= n; i++) ret = (ret + b(i)) % mo;
	//assert((ret + mo) % mo == (rv + mo) % mo);
	return rv;
}

ll sumu(ll x) {
	return smu[id(x)] + 1;
}

ll T(ll x) {
	return aT[id(x)] + 1;
}

int main() {
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	cin >> n >> c; init(1e6); min25();
	A1 = 2 * ksm(c - 1, mo - 2) % mo;
	B1 = - (1 + A1) * ksm(c - 1, mo - 2) % mo;
	A2 = 1 * ksm(c - 1, mo - 2);
	B2 = - A2 * ksm(c - 1, mo - 2) % mo;
	for(int l = 1; l <= n; ) {
		int r = n / (n / l);
		ans = (ans + (sumg(r) - sumg(l - 1)) * T(n / l)) % mo;
		ans = (ans - (sumb(r) - sumb(l - 1)) * sumu(n / l)) % mo;
		l = r + 1;
	}
	cout << (ans + mo) % mo << endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值