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

9 篇文章 0 订阅
8 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
信息学奥赛是一项重要的竞赛活动,旨在培养学生的计算机科学和信息技术能力。省选是指在全省范围内举办的预赛,参赛选手需要通过省选才能晋级到更高层次的比赛。在省选中发挥出色的选手将有机会代表本省参加全国信息学奥林匹克竞赛(NOI)。 而noi_pdf-2020.12.29.rar 是一个压缩文件的命名,其中包含了一份关于NOI的PDF文档。这个文件可能包含了有关NOI的相关资料,例如竞赛规则、题目类型、考试要求等等。通过研究这个文件,选手可以更好地准备信息学奥赛,提高竞赛成绩。 对于想要参加信息学奥赛的同学们来说,可以利用这份PDF文档来深入了解NOI的要求和考试内容。首先,可以仔细阅读竞赛规则,了解比赛的时间、地点、参赛资格等重要信息。其次,可以通过研究题目类型和考试要求,明确自己需要学习和复习的内容,制定合理的备考计划。此外,可以通过查阅往年的竞赛题目和解答,进行练习和模拟考试,提高解题能力和应变能力。 综上所述,信息学奥赛省选和noi_pdf-2020.12.29.rar对于想要参加信息学奥赛的同学们来说都有重要的意义。省选是选拔出优秀选手的一个重要阶段,而noi_pdf-2020.12.29.rar则提供了有关NOI的重要资料,帮助选手更好地准备竞赛。希望通过努力学习和准备,同学们可以在比赛中取得优异成绩,提升自己的计算机科学和信息技术能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值