题目链接

题意简述

你有一个长度为 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. 开发一个Flink应用

    步骤列表本次实战经历以下步骤: 创建应用:编码:构建:提交任务到Flink,验证功能: 环境信息Flink:1.7:Flink所在机器的操作系统:CentOS Linux release 7.5.18 ...

  2. 测开之路一百三十九:会话管理之cookie写入、读取、和更新

    机制:服务器端发送的小段文本信息存储在客户端硬盘 功能:记录用户偏好,请求.页面.站点间共享信息 特点:易丢失.安全隐患 添加cookie,需要用到make_respons.set_cookie @a ...

  3. delphi2010:按键 控制键 组合键的判断 响应

    procedure TForm7.FormShortCut(var Msg: TWMKey; var Handled: Boolean); var   aKey: TShortCut;   aShif ...

  4. Java ——接口

    本节重点思维导图 定义: public interface Traffic { public static final int sits = 4; public abstract void run() ...

  5. 机器学习【一】K最近邻算法

    K最近邻算法 KNN 基本原理 离哪个类近,就属于该类   [例如:与下方新元素距离最近的三个点中,2个深色,所以新元素分类为深色] K的含义就是最近邻的个数.在sklearn中,KNN的K值是通过n ...

  6. maven指定本地jar包

    来自 https://blog.csdn.net/zhengxiangwen/article/details/50734565 一.怎么添加jar到本地仓库呢?步骤:1.cmd命令进入该jar包所在路 ...

  7. CentOS7 策略路由配置

    环境说明:Cloud1中的GE0/0/1.GE0/0/3.GE0/0/5接口,分别与Centos7中的eth1.eth2.eth3接口桥接到同一虚拟网卡,R1,R2,R3均配置一条静态默认路由指向Ce ...

  8. Java——ArrayList使用Demo

    三种遍历方式 通过迭代器Iterator遍历 通过get(索引值)遍历 for循环遍历 ArrayList使用Demo package list; import java.util.ArrayList ...

  9. Square HDU 1518 搜索

    Square HDU 1518 搜索 题意 原题链接 给你一定若干个木棒,让你使用它们组成一个四边形,要求这些木棒必须全部使用. 解题思路 木棒有多种组合方式,使用搜索来进行寻找,这里需要进行优化,不 ...

  10. [Luogu 5465] [LOJ 6435] [PKUSC2018]星际穿越(倍增)

    [Luogu 5465] [LOJ 6435] [PKUSC2018]星际穿越(倍增) 题面 n个点的图,点i和[l[i],i)的所有点连双向边.每次询问(l,r,x)表示x到[l,r]的所有点的最短 ...