题解 P4156 【[WC2016]论♂战♂捆竹竿】
PhantasmDragon · · 题解
神仙题 QAQ
先给个定义:
一个字符串的
在本题中,显然我们可以选择
那就把题目转化为了一个容量为
那么暴力哈希求
啥?你不知道什么是同余 BFS? 可以去看一下洛谷这道题: 跳楼机 再来做这道题qwq
然后就有一个很神仙的结论: 把
证明请看官方题解, 我太菜了,不会证.
划分等差数列的意思嘛,举个栗子:
那么现在就只用考虑如何用一个等差数列来转移以及不同等差数列的合并.
假设有一个首项为
因为是在模
那么在模
这样我们就做完了每个等差数列的转移. 但是还需要把每次算出来的
我们把新的
为啥? 我们在原来的剩余系
但是原来在模
别人大佬的博客上并没有讲为啥可以这样转移,以上纯属个人yy,如果有错,欢迎指出qwq
那么这样就在
贴上代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define int long long
#define ll long long
#define maxn 1000005
using namespace std;
ll Seed=998244353ll;
ll Ha[maxn],pw[maxn];
char s[maxn];
int n,W,Bo[maxn],f[maxn],pre[maxn],now;
void GetBorder()
{
Bo[0]=0;
for(int i=1;i<=n;i++)
Ha[i]=Ha[i-1]+pw[i]*s[i];
for(int i=1;i<n;i++)
if(Ha[i]*pw[n-i]==Ha[n]-Ha[n-i]) Bo[++Bo[0]]=i;
for(int i=1;i<=Bo[0];i++) Bo[i]=n-Bo[i];
reverse(Bo+1,Bo+Bo[0]+1);
}
int seq[maxn];
void ChangeMod(int x)
{
int D=__gcd(x,now),top=0;
for(int i=0;i<now;i++) pre[i]=f[i];
for(int i=0;i<x;i++) f[i]=1e18;
for(int i=0,tmp;i<now;i++)
tmp=pre[i]%x,f[tmp]=min(f[tmp],pre[i]);
for(int i=0;i<D;i++)
{
top=0,seq[++top]=i;
int tmp=(i+now)%x;
while(tmp!=seq[1])
seq[++top]=tmp,tmp=(tmp+now)%x;
for(int j=1;j<=top;j++)
seq[j+top]=seq[j];
top<<=1;
for(int j=2;j<=top;j++)
f[seq[j]]=min(f[seq[j]],f[seq[j-1]]+now);
}
now=x;
}
int dq[maxn],w[maxn];
void Solve(int x0,int d,int len)
{
int D=__gcd(x0,d),top,l,r,N=0;
static int Q[maxn];
ChangeMod(x0);
if(d<0) return;
for(int i=0;i<D;i++)
{
N=0,top=0,Q[++top]=i;
int tmp=(i+d)%x0;
while(tmp!=Q[1])
Q[++top]=tmp,tmp=(tmp+d)%x0;
int st=1;
for(int i=1;i<=top;i++) if(f[Q[i]]<f[Q[st]]) st=i;
for(int i=st;i<=top;i++) seq[++N]=Q[i];
for(int i=1;i<st;i++) seq[++N]=Q[i];
l=1,r=1,dq[1]=1,w[1]=f[seq[1]]-d;
for(int pt=2;pt<=top;pt++)
{
while(l<=r&&dq[l]+len<pt) l++;
if(l<=r) f[seq[pt]]=min(f[seq[pt]],w[l]+pt*d+x0);
while(l<=r&&w[r]>=f[seq[pt]]-pt*d) r--;
w[++r]=f[seq[pt]]-pt*d,dq[r]=pt;
}
}
}
int T=0;
signed main()
{
scanf("%lld",&T),pw[0]=1;
for(int i=1;i<maxn;i++) pw[i]=pw[i-1]*Seed;
while(T--)
{
memset(Bo,0,sizeof(Bo));
scanf("%lld%lld%s",&n,&W,s+1),W-=n;
GetBorder(),now=n;
for(int i=1;i<now;i++) f[i]=1e18; f[0]=0;
for(int i=1,j=1;i<=Bo[0];i=j)
{
while(Bo[i+1]-Bo[i]==Bo[j+1]-Bo[j]) j++;
Solve(Bo[i],Bo[i+1]-Bo[i],j-i-1);
}
int ans=0;
for(int i=0;i<now;i++)
if(W-f[i]>=0) ans+=(W-f[i])/now+1;
printf("%lld\n",ans);
}
return 0;
}