jzoj6042 [NOI2019五校联考2019.3.5]Second 后缀数组+分治+rmq

本文探讨了一种使用后缀数组解决特定字符串处理问题的方法,通过寻找区间内高度的最小值来优化解决方案。文章深入分析了最优答案与分配的k值之间的关系,并提出了一种基于此原理的高效算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description


在这里插入图片描述

Solution


没想到。。真没想到,以为是退火或者消元的
后缀数组的板子都不记得了

两个lcp实际上就是区间height的最小值,我们每次取出最小值的位置考虑

有一个很重要的结论是对于某一段,最优答案与分配的k值之和是成正比的,也就是各k值之间是成固定比例的。关于这一点我并不会证明,希望能有大爷告诉我啥的。。
也就是说我们并不需要知道具体某一段分配了多少,只需要给左区间分配x,那么右区间分配1-x,最后算出来的答案各自乘上分配的系数即可

设左区间答案为a,右区间答案为b,左区间分配x,我们实际上要求 max ⁡ ( a x + ( 1 − x ) h , ( 1 − x ) b + x h ) \max(ax+(1-x)h,(1-x)b+xh) max(ax+(1x)h,(1x)b+xh)
注意到两边都是单调的,因此最大值取在它们相等的时候

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))

const int N=2000005;

char str[N];

int sa[N],rank[N],s2[N],r1[N],ct[N],h[N];
int mn[21][N],n;

void pre() {
	fill(ct,0);
	rep(i,1,n) ct[rank[i]=str[i]-'a'+1]++;
	rep(i,1,26) ct[i]+=ct[i-1];
	drp(i,n,1) sa[ct[rank[i]]--]=i;
	for (int j=1,k=0,mx=26;k<n;j<<=1,mx=k) {
		int p=0;
		rep(i,n-j+1,n) s2[++p]=i;
		rep(i,1,n) if (sa[i]>j) s2[++p]=sa[i]-j;
		fill(ct,0);
		rep(i,1,n) ct[rank[s2[i]]]++;
		rep(i,1,mx) ct[i]+=ct[i-1];
		drp(i,n,1) sa[ct[rank[s2[i]]]--]=s2[i];
		r1[sa[1]]=k=1;
		rep(i,2,n) {
			if (rank[sa[i-1]]==rank[sa[i]]&&rank[sa[i-1]+j]==rank[sa[i]+j]) {
				r1[sa[i]]=k;
			} else r1[sa[i]]=++k;
		}
		rep(i,1,n) rank[i]=r1[i];
	}
	h[1]=0;
	for (int i=1,j=0;i<=n;++i) {
		if (rank[i]==1) continue;
		j=std:: max(h[rank[i-1]]-1,0);
		while (str[i+j]==str[sa[rank[i]-1]+j]) j++;
		h[rank[i]]=j;
	}
	rep(i,1,n) mn[0][i]=i;
	rep(j,1,20) {
		int len=1<<j-1;
		rep(i,1,n-len+1) {
			if (h[mn[j-1][i]]<=h[mn[j-1][i+len]]) {
				mn[j][i]=mn[j-1][i];
			} else mn[j][i]=mn[j-1][i+len];
		}
	}
}

int query(int l,int r) {
	// l=rank[l],r=rank[r];
	// if (l>r) std:: swap(l,r); 
	l++;
	int lg=log2(r-l+1);
	if (h[mn[lg][l]]<=h[mn[lg][r-(1<<lg)+1]]) return mn[lg][l];
	return mn[lg][r-(1<<lg)+1];
}

double solve(int l,int r) {
	if (l==r) return n-sa[l]+1;
	int mid=query(l,r);
	double a=solve(l,mid-1),b=solve(mid,r);
	double x=(b-h[mid])/(a+b-2*h[mid]);
	return a*x+h[mid]*(1-x);
}

int main(void) {
	freopen("second.in","r",stdin);
	freopen("second.out","w",stdout);
	scanf("%s",str+1);
	n=strlen(str+1);
	pre();
	double ans=solve(1,n);
	printf("%.6lf\n", ans);
	return 0;
}
### 计算从1到N的阶乘之和 在编程竞赛中,计算从1到N的阶乘之和是一个常见的题目。以下是实现该功能的方法以及一些注意事项。 #### 方法描述 可以通过循环来逐步累加每个数的阶乘值。由于阶乘增长非常迅速,因此需要注意数据类型的选取以防止溢出。通常情况下,在C++中可以选择`unsigned long long`作为存储结果的数据类型[^3]。 下面提供了一个基于C++的标准解决方案: ```cpp #include <iostream> using namespace std; int main() { unsigned int n; cout << "Enter a positive integer: "; cin >> n; unsigned long long sum = 0; // 存储最终的结果 unsigned long long factorial = 1; // 当前的阶乘值 for (unsigned int i = 1; i <= n; ++i) { factorial *= i; // 更新当前阶乘值 sum += factorial; // 累加至总和 } cout << "Sum of factorials from 1 to " << n << " is: " << sum << endl; return 0; } ``` 此代码片段展示了如何通过简单的迭代方法完成任务。注意这里使用了`unsigned long long`以适应较大的数值范围。 对于Python而言,其内置的大整数支持使得处理此类问题更加简便: ```python def factorial_sum(n): total, fact = 0, 1 for i in range(1, n + 1): fact *= i # Update the current factorial value. total += fact # Add it to the running total. return total n = int(input("Enter a number: ")) print(f"The sum of factorials up to {n} is:", factorial_sum(n)) ``` 上述Python版本无需担心数据类型的选择问题,因为Python自动管理大整数运算[^1]。 #### 性能优化建议 当面对更大的输入规模时,应考虑算法的时间复杂度与空间复杂度。尽管本题目的直接解法已经足够高效,但在更复杂的场景下可能还需要引入动态规划或其他高级技巧[^2]。 另外,利用标准模板库(STL),如向量(vector)或者列表(list),可以帮助更好地管理和操作大量中间结果。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值