题目链接

题意简述

你有一个长度为 n 的字符串 , 将它复制任意次 , 复制出的串的前缀可以与之前的串的后缀重叠在一起 , 问最后总共可能的长度数目 , 长度不能超过 \(w\)

多组数据。

\(n\leq 5*10^5 ,w\leq 10^{18}\)

Sol

显然每次可以重叠的部分是原串的一个 boder

假设这个boder长度为 \(L\) , 那么长度可以只增加 \(n-L\)

那么就是一个存在性完全背包问题。

那么先求出所有boder , 之后显然可以用同余类最短路来求答案。

不过这个样子复杂度太高了。

最短路的做法是有所多余的 , 每一个点只会向后连边 , 连边的模式也是一定的 , 最短路和转移之间的顺序就是没有关系的 , 我们可以分开考虑每一种连边方式 , 假设这个长度为 \(L\) 。

由数学知识我们可以知道他将原来的长度为 \(n\) 的数组分成了 \(gcd(n,L)\) 个在这次转移中互不影响的环 , 我们在每一个环里找到 距离最小的那个点开始转移就可以了。

这样复杂度就是 \(O(T*n^2)\) , 只有 30'

接下来就要用到一个很强的性质了 , 一个字符串的 \(boder\) 形成的等差数列的个数不会超过 \(log\) 个

证明不会。

这又什么用呢?

考虑一个等差数列在转移的时候有什么优秀的性质。

我们假设首项为\(x\),公差为 \(d\) ,长度为 \(l\)

那么假设我们在模 \(x\) 的意义下进行这些转移 , 这些长度都可以等价于一个长度为 \(d\) 的转移方式!这样我们要考虑的转移方式总数就下降到了 \(log\) 级别。由于长度只有 \(l\) ,我们转移的时候最多就只能从前 \(l-1\) 个转移过来 , 用一个单调队列来维护转移即可。

但是这样还有一个问题 , 因为这样同余类最短路的模数不就一直在变?

其实是可以转换的 , 具体方式为:

假设新的模数 \(p\) 下的dp数组为 \(g\) , 原来的模数为 \(q\) ,数组为 \(f\) ,那么首先可以 \(g[f[i]\%p]=f[i]\) , 这样有什么问题呢?

容易发现 , 由于之前我们相当于是没有进行 长度为 \(q\) 的转移的 , 所以这里的 dp 数组会有些值是不对的 , 这样的话我们重新进行一次长度为 \(q\) 的转移就好了。

code:

#include<bits/stdc++.h>
using namespace std;
#define next NEXTT
#define Set(a,b) memset(a,b,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(a))
const int N=5e5+10;
typedef long long ll;
char S[N];
int next[N];
int T;
ll n,w,ans,f[N],g[N];
int len[N],tot=0,Idex,vis[N];
int head,tail;
int que[N],V[N];
inline void Update(int pL,int nL,int d,int cnt){
Set(g,-1);
for(int i=0;i<pL;++i) if(~f[i]) {int res=f[i]%nL;if(~g[res]) g[res]=min(g[res],f[i]);else g[res]=f[i];}
Copy(f,g);
++Idex;
for(int i=0;i<nL;++i) {
if(vis[i]==Idex) continue;
int mip=i;vis[i]=Idex;
for(int j=(i+pL)%nL;j^i;j=(j+pL)%nL) {
if((!~f[mip])||((~f[j])&&f[mip]>f[j])) mip=j;
vis[j]=Idex;
}
if(~f[mip]){
for(int pre=mip,j=(mip+pL)%nL;j^mip;pre=j,j=(j+pL)%nL){if(!~f[j]) f[j]=f[pre]+pL;else f[j]=min(f[j],f[pre]+pL);}
}
}++Idex;
for(int i=0;i<nL;++i) {
if(vis[i]==Idex) continue;
int mip=i;vis[i]=Idex;
for(int j=(i+d)%nL;j^i;j=(j+d)%nL){
if((!~f[mip])||((~f[j])&&f[j]<f[mip])) mip=j;
vis[j]=Idex;
}
if(!~f[mip]) continue;
head=1,tail=1;que[1]=1;V[1]=mip;
for(int k=2,j=(mip+d)%nL;j^mip;j=(j+d)%nL,++k){
while(head<=tail&&k-que[head]>=cnt) ++head;
if(head<=tail) {if(~f[j]) f[j]=min(f[j],f[V[head]]+(k-que[head])*d+nL);else f[j]=f[V[head]]+(ll)(k-que[head])*d+nL;}
if(~f[j]){
while(head<=tail&&f[j]<=(ll)d*(k-que[tail])+f[V[tail]]) --tail;
que[++tail]=k,V[tail]=j;
}
}
}
return;
}
void Solve(){
int lst=n;--tot;
reverse(len+1,len+1+tot);
int L;
for(int i=1,j;i<=tot;i=j) {
j=i+1;int d=len[i]-len[j];
if(j>tot) d=0;
while(j<=tot&&len[j-1]-len[j]==d) ++j;
L=len[j-1];Update(lst,L,d,j-i);lst=L;
}
ans=0;
for(int j=0;j<lst;++j) if(~f[j]&&f[j]<=w) ans+=(w-f[j])/lst+1;
return;
} int main()
{
#ifndef ONLINE_JUDGE
freopen("jie.in","r",stdin);
freopen("jie.out","w",stdout);
#endif
scanf("%d",&T);
while(T--){
Set(vis,0);Idex=0;
scanf("%lld %lld",&n,&w);
ans=0;w-=n;scanf("%s",S+1);
register int i,j=0;Set(next,0);
for(i=2;i<=n;++i) {
while(j&&S[i]!=S[j+1]) j=next[j];
if(S[i]==S[j+1]) ++j;next[i]=j;
}j=n;tot=0;
while(j){len[++tot]=n-next[j];j=next[j];}
Set(f,-1),f[0]=0,Solve();
printf("%lld\n",ans);
}
}

【LuoguP4156】论战捆竹竿的更多相关文章

  1. 「WC2016」论战捆竹竿

    「WC2016」论战捆竹竿 前置知识 参考资料:<论战捆竹竿解题报告-王鉴浩>,<字符串算法选讲-金策>. Border&Period 若前缀 \(pre(s,x)​\ ...

  2. UOJ#172. 【WC2016】论战捆竹竿 字符串 KMP 动态规划 单调队列 背包

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ172.html 题解 首先,这个问题显然是个背包问题. 然后,可以证明:一个字符串的 border 长度可 ...

  3. luogu P4156 [WC2016]论战捆竹竿

    传送门 官方题解(证明都在这) 神仙题鸭qwq 转化模型,发现这题本质就是一个集合,每次可以加上集合里的数,问可以拼出多少不同的数 首先暴力需要膜意义下的最短路,例题戳这 然后这个暴力可以优化成N^2 ...

  4. Luogu4156 WC2016 论战捆竹竿 KMP、同余类最短路、背包、单调队列

    传送门 豪华升级版同余类最短路-- 官方题解 主要写几个小trick: \(1.O(nm)\)实现同余类最短路: 设某一条边长度为\(x\),那么我们选择一个点,在同余类上不断跳\(x\),可以形成一 ...

  5. UOJ#172. 【WC2016】论战捆竹竿

    传送门 首先这个题目显然就是先求出所有的 \(border\),问题转化成一个可行性背包的问题 一个方法就是同余类最短路,裸跑 \(30\) 分,加优化 \(50\) 分 首先有个性质 \(borde ...

  6. bzoj4406: [Wc2016]论战捆竹竿&&uoj#172. 【WC2016】论战捆竹竿

    第二次在bzoj跑进前十竟然是因为在UOJ卡常致死 首先这个题其实就是一个无限背包 一般做法是同余最短路,就是bzoj2118: 墨墨的等式可以拿到30分的好成绩 背包是个卷积就分治FFT优化那么下面 ...

  7. BZOJ4406 WC2016 论战捆竹竿

    Problem BZOJ Solution 显然是一个同余系最短路问题,转移方案就是所有|S|-border的长度,有 \(O(n)\) 种,暴力跑dijkstra的复杂度为 \(O(n^2\log ...

  8. 【WC2016】论战捆竹竿

    已经快三周了啊--终于把挖的坑填了-- 首先显然是把除了自身的所有border拿出来,即做 \(\left\{ n - b_1, n - b_2, \dots, n - b_k, n \right\} ...

  9. WC2016自测

    挑战NPC 原题链接 爆搜20分,贪心10分,网络流30分 //挑战NPC #include <cstdio> #include <cstring> #include < ...

随机推荐

  1. Git - grafted 和 shallow update not allowed

    一般人对开源的模板进行修改是总会进行这样的一条龙操作 # 克隆最近一次提交 git clone xxx --depth 1 # 修改修改修改 提交提交提交 vim xxx git commit -am ...

  2. Mac下安装lightgbm

    Mac下安装lightgbm 1.安装环境 系统 MacOS Mojave 版本10.14.2 Xcode 10.1 $ clang -v Apple LLVM version 10.0.0 (cla ...

  3. OpenStack Nova 高性能虚拟机之 NUMA 架构亲和

    目录 文章目录 目录 写在前面 计算平台体系结构 SMP 对称多处理结构 NUMA 非统一内存访问结构 MPP 大规模并行处理结构 Linux 上的 NUMA 基本对象概念 NUMA 调度策略 获取宿 ...

  4. 用configmap管理配置

    一.ConfigMap介绍管理配置: ConfigMap介绍 Secret 可以为 Pod 提供密码.Token.私钥等敏感数据:对于一些非敏感数据,比如应用的配置信息,则可以用 ConfigMap ...

  5. 【AOP】操作相关术语---【Spring】的【AOP】操作(基于aspectj的xml方式)

    [AOP]操作相关术语 Joinpoint(连接点):类里面哪些方法可以被增强,这些方法称为连接点. Pointcut(切入点):在类里面可以有很多的方法被增强,比如实际操作中,只是增强了类里面add ...

  6. c++ 创建 uuid guid

    如果没安装,先安装: [root@localhost]# yum install libuuid-devel #include "uuid/uuid.h" 引用 libuuid.s ...

  7. RabbitMQ使用(上)

    1. 说明 在企业应用系统领域,会面对不同系统之间的通信.集成与整合,尤其当面临异构系统时,这种分布式的调用与通信变得越发重要.其次,系统中一般会有很多对实时性要求不高的但是执行起来比较较耗时的地方, ...

  8. 批处理文件将多台连接的手机安装同一个APP

    :adb devices > C:\Users\aaminaxu\Desktop\a.txt :1.首先获取连接到电脑的手机的设备信息将其保存到C盘的a.txt文件中::2.将保存的txt文件中 ...

  9. 【Linux开发】OpenCV在ARM上的移植

    与X86 Linux类似,请参考:Linux 下编译安装OpenCV 本文在此基础上进行进一步操作. 网络上很多移植编译的方法比较老,多数针对OpenCV 1.0,而且方法很麻烦,不仔细操作很容易出错 ...

  10. 【Linux开发】V4L2应用程序框架

    V4L2应用程序框架 V4L2较V4L有较大的改动,并已成为2.6的标准接口,函盖video\dvb\FM...,多数驱动都在向V4l2迁移.更好地了解V4L2先从应用入手,然后再深入到内核中结合物理 ...