[JSOI2015]串分割 解题报告

题目背景

JYY 每天都会在地铁上度过很长的时间。

为了打发时间,JYY 随手写下了一个很长的环形的数字字符串,并且陷入了沉思。

题目描述

JYY 写下了一个长度为 \(N\) 的,仅包含 12,……,9\(9\) 种不同字符的环形字符串 \(S\)。JYY 希望把 \(S\) 进行 \(K\) 次切割,并分成 \(K\) 个非空的子串。对于每一个子串,由于其仅包含数字,我们可以将其看成一个十进制数——因此
经过 \(K\) 次切割,JYY 可以得到 \(K\) 个不同的十进制数。JYY 希望他得到的这 \(K\) 个数中,最大的那一个尽量小。

输入格式

第一行包含两个整数 \(N\)\(K\)

第二行包含一个长度为 \(N\) 的字符串 \(S\)

输出格式

输出一行包含一个正整数,表示最佳分割方案中,JYY 所得到的那 \(K\) 个数中,最大的那一个。

样例 #1

样例输入 #1

4 2
4321

样例输出 #1

32

提示

对于 \(100\%\) 的数据,\(3\leq N\leq 2\times 10^5\)\(2\leq K\leq N\)

思路

由于要划分最大的最小(二分!)

因此我们划分的段们长度尽可能平均

我们考虑二分最大的那一节的开头

要求后缀数组\(rk\)

因为最大的要最小,所以我们根据\(rk\)值来二分!!

显然要使最大的那一节的开头\(rk\)尽可能小

这样答案就满足单调性啦(。・∀・)ノ゙

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=4e5+2;
char s[N];
ll rk[N],sa[N],c[N],x[N],y[N],tot,hei[N],n,k,len;
void getsa(int m){//后缀数组
	for(ll i=1;i<=m;i++)c[i]=0;
	for(ll i=1;i<=n;i++)c[x[i]=s[i]]++;
	for(ll i=2;i<=m;i++)c[i]+=c[i-1];
	for(ll i=n;i>=1;i--)sa[c[x[i]]--]=i;
	for(ll k=1;k<=n;k<<=1){
		ll tot=0;
		for(ll i=n-k+1;i<=n;i++)y[++tot]=i;
		for(ll i=1;i<=n;i++){
			if(sa[i]>k)y[++tot]=sa[i]-k;
		}
		for(ll i=1;i<=m;i++)c[i]=0;
		for(ll i=1;i<=n;i++)c[x[i]]++;
		for(ll i=2;i<=m;i++)c[i]+=c[i-1];
		for(ll i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i];
		swap(x,y);
		x[sa[1]]=1,tot=1;
		for(ll i=2;i<=n;i++){
			x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?tot:++tot;
		}
		if(tot==n)break;
		m=tot;
	} 
}
void gethei(){//hei[i]:排名为i和排名前一个的lcp 
	for(ll i=1;i<=n;i++)rk[sa[i]]=i;
	for(ll i=1,k=0;i<=n;i++){
		if(rk[i]==1)continue;
		if(k)k--;
		ll j=sa[rk[i]-1];//排名前面一个
		while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
		hei[rk[i]]=k; 
	}
}
bool check(ll mid,ll m){
	bool ok=0;
	for(ll i=1;i<=len;i++){
      	ll cnt=0;//length长度 
      	for(ll j=1;j<=k;j++){//k段 
        	ll p=(i+cnt-1)%m+1;
       		if(rk[p]<=mid) cnt+=len;//数小的长度大→最大的尽量小 
        	else cnt+=len-1;
      	}
      	ok|=(cnt>=m);
    }
    return ok;
}
int main(){
	scanf("%lld%lld%s",&n,&k,s+1);
	len=(n-1)/k+1;//长度要平均 
  	for(ll i=1;i<=n;i++){
		s[i+n]=s[i];
	} 
  	n<<=1;//破环为链 
	getsa(127);
	gethei();
  	ll l=1,r=n;
  	while(l<r){
    	ll mid=(l+r)>>1;
    	if(check(mid,n>>1)) r=mid;
    	else l=mid+1;
  	}
  	for(ll i=1;i<=len;i++){
  		printf("%c",s[sa[l]+i-1]);
  	}
  	return 0;
}

完结撒花❀

★,°:.☆( ̄▽ ̄)/$:.°★

posted @ 2023-02-01 19:54  _Youngxy  阅读(52)  评论(0)    收藏  举报