P4156 [WC2016]论战捆竹竿 题解
题意描述#
给定一个字符串
题目分析#
首先可以看出,抛开字符串,把每个可以拼上去的长度筛选出来,问题就转化为有
模仿通常同余最短路的操作,从每个点延伸
我们考虑优化,既然边这么多,能不能去掉一些没有用的部分,反过来考虑字符串 Border 的一些性质,注意到周期好像是个很有用的部分,能把冗余部分压缩,我们试图从这方面考虑。
对于周期和 Border,我们有一个很显然的性质:每个长度大于
因为
因为所有的周期都能由最小周期组成,所以所有大于
这个性质很有用,我们可以把每条数列单独拉出来跑同余最短路,优化边数即可达到目的。
具体实现#
把最小的当成模数,每个点就只有一条出边,整个图总共会形成
接着把环分离,从最小值开始转圈跑最短路,最后跑下一条等差数列的时候改变一下模数即可(如果从小到大跑只用把每个最小值所在位置,改成模新模数的位置,并查看前面会不会对后面增加的同余类产生影响即可)。
一共有
代码#
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<cstring>
#include<queue>
#define N 500005
using namespace std;
typedef unsigned long long ll;
ll cnt,bor[N<<3],nex[N<<3];
ll res[N<<3];
ll dis[N<<3],lim;
struct Node{
ll num,val;
};
ll top,Q[N<<3],q2[N<<3];
char s[N];
ll gcd(ll x,ll y){
if(!y) return x;
else return gcd(y,x%y);
}
void get_border(ll n){
ll ans=0;
for(ll i=2;i<=n;i++){
while(ans && s[ans+1]!=s[i]) ans=nex[ans];
if(s[ans+1]==s[i]) ans++;
nex[i]=ans;
}
while(ans){bor[++cnt]=n-ans;ans=nex[ans];}
bor[++cnt]=n;
}
void rfs(ll len){
ll siz=gcd(lim,len);
for(ll i=0;i<lim;i++)res[i]=dis[i];
for(ll i=0;i<len;i++)dis[i]=0x7f7f7f7f7f7f7f7f;
for(ll i=0;i<lim;i++){
ll tmp=res[i]%len;
dis[tmp]=min(dis[tmp],res[i]);
}
for(ll i=0;i<siz;i++){
top=0;Q[++top]=i;ll tmp=(i+lim)%len;
while(tmp!=i){Q[++top]=tmp;tmp=(tmp+lim)%len;}
for(ll j=top+1;j<=top*2;j++) Q[j]=Q[j-top];
for(ll j=2;j<=(top<<1);j++) dis[Q[j]]=min(dis[Q[j]],dis[Q[j-1]]+lim);
}
lim=len;
}
void update(ll a,ll d,ll num){
if(d<0) return;
ll siz=gcd(lim,d);
for(ll i=0;i<siz;i++){
top=0;ll tmp=(i+d)%lim;Q[++top]=i;
while(tmp!=i){Q[++top]=tmp;tmp=(tmp+d)%lim;}
tmp=1;
for(ll j=2;j<=top;j++) if(dis[Q[j]]<dis[Q[tmp]]) tmp=j;
for(ll j=1;j<=top;j++) res[j]=Q[j];
for(ll j=tmp;j<=top;j++) Q[j-tmp+1]=res[j];
for(ll j=1;j<tmp;j++) {Q[j+(top-tmp+1)]=res[j];}
ll head=1,tail=1;q2[1]=1;
for(ll j=2;j<=top;j++){
while(head<=tail && j-q2[head]>num) head++;
dis[Q[j]]=min(dis[Q[j]],dis[Q[q2[head]]]+a+d*(j-q2[head]));
while(tail>=head && dis[Q[q2[tail]]]+d*(j-q2[head])>dis[Q[j]]) tail--;
q2[++tail]=j;
}
}
}
ll solve(ll n,ll w){
ll L=1;
while(L<=cnt){
ll R=L;
while(bor[R+1]-bor[R]==bor[L+1]-bor[L]) R++;
rfs(bor[L]);
if(R>cnt) {L=R;continue;}
update(bor[L],(L==R?0:bor[L+1]-bor[L]),R-L);
L=R;
}
ll ans=0;
for(ll i=0;i<lim;i++){
if(dis[i]>w) continue;
ans+=(w-dis[i])/lim+1;
}
return ans;
}
int main(){
ll t;
cin>>t;
while(t--){
ll n,w;
cin>>n>>w>>(s+1);if(w<n){cout<<0<<endl;continue;}cnt=0;w-=n;
lim=n;dis[0]=0;
get_border(n);
cout<<solve(n,w)<<endl;
for(ll i=0;i<n;i++) dis[i]=0x7f7f7f7f7f7f7f7f;
}
}
作者:eastcloud
出处:https://www.cnblogs.com/eastcloud/p/17272391.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?