UOJ#172. 【WC2016】论战捆竹竿 字符串 KMP 动态规划 单调队列 背包
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ172.html
题解
首先,这个问题显然是个背包问题。
然后,可以证明:一个字符串的 border 长度可以划分成 $O(log |S|)$ 个等差数列。
(以下图片摘自 金策 - 《字符串算法选讲》)
由于长度 n 可以随便取,所以我们可以在对n取模的意义下做背包,设 dis[i] 为 占用背包容量%n = i 时至少要占用多少背包容量,那么直接建 $n^2$ 条边跑一下 dijkstra 就可以在 $O(n^2\log n )$ 的时间复杂度内解决此题。
对于所有的长度,我们将其分组,保证每一组是一个等差数列。
我们枚举等差数列,依次将其加入背包。
假设最终背包中最多包含当前等差数列的一个元素,那么比较好做,直接单调队列就好了。
但是可以有多个当前等差数列的元素计入最终背包容量。也就是说,假设当前等差数列用 $v + kd$ 表示,那么可能会有 $tv + \sum k_i d$ 这种情况。这在对 n 取模的意义下非常难做。
于是我们考虑把它转化成对 $v$ 取模的意义下的结果,这样就方便多了。
考虑如何转化:先把原先在对 n 取模意义下的结果放到对 v 取模意义下的数组里,然后考虑每一个 $dis'[i]$ 都可以更新 $dis'[(i+n)\mod v ]$,即 $dis'[(i+n)\mod v] =min(dis'[(i+n)\mod v],dis'[i] + n)$ 。
把转移形成的环搞出来,dp转移一下就可以得到新的dis数组。
剩下的单调队列+dp也类似。
于是时间复杂度是 $O(n\log n)$ 。
代码
#pragma GCC optimize("Ofast","inline")
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define fi first
#define se second
using namespace std;
typedef long long LL;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=500005;
const LL INF=2e18;
int T,n,m;
LL w;
int Fail[N],nf[N];
struct brd{
int v,d,c;
}b[N];
char s[N];
LL dis[N],rng;
LL gcd(LL a,LL b){
return b?gcd(b,a%b):a;
}
void Trans(LL nr){
static LL d[N];
for (int i=0;i<nr;i++)
d[i]=INF;
for (int i=0;i<rng;i++)
d[dis[i]%nr]=min(d[dis[i]%nr],dis[i]);
int g=gcd(rng,nr);
for (int t=0;t<g;t++){
int e=nr/g*2,_i=t,rg=rng%nr;
for (LL i=t,j=0;j<e;i+=rng,j++){
d[_i]=min(d[_i],d[_i<rg?_i-rg+nr:_i-rg]+rng);
if ((_i+=rg)>=nr)
_i-=nr;
}
}
rng=nr;
for (int i=0;i<rng;i++)
dis[i]=d[i];
}
pair <LL,LL> Q[N*2];
int head,tail;
void DP(int d,int c){
int g=gcd(rng,d);
for (int t=0;t<g;t++){
head=1,tail=0;
int e=rng/g*2,_i=t;
for (LL i=t,j=0;j<e;i+=d,j++){
if (head<=tail&&Q[head].fi+(LL)c*d<=i)
head++;
if (head<=tail)
dis[_i]=min(dis[_i],Q[head].se+rng+i);
while (head<=tail&&Q[tail].se>=dis[_i]-i)
tail--;
Q[++tail]=make_pair(i,dis[_i]-i);
if ((_i+=d)>=rng)
_i-=rng;
}
}
}
int main(){
T=read();
while (T--){
n=read(),w=read();
scanf("%s",s+1);
Fail[1]=0;
for (int i=2;i<=n;i++){
int k=Fail[i-1];
while (k>0&&s[k+1]!=s[i])
k=Fail[k];
if (s[k+1]==s[i])
k++;
Fail[i]=k;
}
m=0;
for (int i=n;i>0;i=Fail[i])
nf[Fail[i]]=i;
for (int j=0;j<n;j=nf[j]){
int i=n-j;
if (j==0)
m++,b[m].v=i,b[m].d=0,b[m].c=1;
else if (!b[m].d)
b[m].d=b[m].v-i,b[m].c++,b[m].v=i;
else if (b[m].v-b[m].d==i)
b[m].v=i,b[m].c++;
else
m++,b[m].v=i,b[m].d=0,b[m].c=1;
}
if (!b[m].d)
b[m].d=1;
clr(dis);
rng=n;
for (int i=0;i<n;i++)
dis[i]=INF;
dis[0]=n;
for (int i=1;i<=m;i++){
Trans(b[i].v);
DP(b[i].d,b[i].c);
}
LL ans=0;
for (int i=0;i<rng;i++)
if (dis[i]<=w)
ans+=w/rng-dis[i]/rng+(w%rng>=dis[i]%rng?1:0);
cout<<ans<<endl;
}
return 0;
}
UOJ#172. 【WC2016】论战捆竹竿 字符串 KMP 动态规划 单调队列 背包的更多相关文章
- bzoj4406: [Wc2016]论战捆竹竿&&uoj#172. 【WC2016】论战捆竹竿
第二次在bzoj跑进前十竟然是因为在UOJ卡常致死 首先这个题其实就是一个无限背包 一般做法是同余最短路,就是bzoj2118: 墨墨的等式可以拿到30分的好成绩 背包是个卷积就分治FFT优化那么下面 ...
- Luogu4156 WC2016 论战捆竹竿 KMP、同余类最短路、背包、单调队列
传送门 豪华升级版同余类最短路-- 官方题解 主要写几个小trick: \(1.O(nm)\)实现同余类最短路: 设某一条边长度为\(x\),那么我们选择一个点,在同余类上不断跳\(x\),可以形成一 ...
- luogu P4156 [WC2016]论战捆竹竿
传送门 官方题解(证明都在这) 神仙题鸭qwq 转化模型,发现这题本质就是一个集合,每次可以加上集合里的数,问可以拼出多少不同的数 首先暴力需要膜意义下的最短路,例题戳这 然后这个暴力可以优化成N^2 ...
- BZOJ4406 WC2016 论战捆竹竿
Problem BZOJ Solution 显然是一个同余系最短路问题,转移方案就是所有|S|-border的长度,有 \(O(n)\) 种,暴力跑dijkstra的复杂度为 \(O(n^2\log ...
- 「WC2016」论战捆竹竿
「WC2016」论战捆竹竿 前置知识 参考资料:<论战捆竹竿解题报告-王鉴浩>,<字符串算法选讲-金策>. Border&Period 若前缀 \(pre(s,x)\ ...
- 【WC2016】论战捆竹竿
已经快三周了啊--终于把挖的坑填了-- 首先显然是把除了自身的所有border拿出来,即做 \(\left\{ n - b_1, n - b_2, \dots, n - b_k, n \right\} ...
- 【LuoguP4156】论战捆竹竿
题目链接 题意简述 你有一个长度为 n 的字符串 , 将它复制任意次 , 复制出的串的前缀可以与之前的串的后缀重叠在一起 , 问最后总共可能的长度数目 , 长度不能超过 \(w\) 多组数据. \(n ...
- UOJ#172. 【WC2016】论战捆竹竿
传送门 首先这个题目显然就是先求出所有的 \(border\),问题转化成一个可行性背包的问题 一个方法就是同余类最短路,裸跑 \(30\) 分,加优化 \(50\) 分 首先有个性质 \(borde ...
- hdu 5510 Bazinga(字符串kmp)
Bazinga Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Sub ...
随机推荐
- HBase轻松入门之HBase架构图解析
2018-12-13 2018-12-20 本篇文章旨在针对初学者以我本人现阶段所掌握的知识就HBase的架构图中各模块作一个概念科普.不对文章内容的“绝对.完全正确性”负责. 1.开胃小菜 关于HB ...
- Django_RBAC_demo2 升级版权限控制组件
RBAC 升级版 预期要求 前端在无权限时不在提供操作标签 更改部分硬编码 实现更加精准的权限控制 未改动前的版本 在这里 ⬇ Django_rbac_demo 权限控制组件框架模型 具体更改 数据库 ...
- 用IntelliJ IDEA 开发Spring+SpringMVC+Mybatis框架 分步搭建三:配置spring并测试
这一部分的主要目的是 配置spring-service.xml 也就是配置spring 并测试service层 是否配置成功 用IntelliJ IDEA 开发Spring+SpringMVC+M ...
- Vue(一)安装
环境准备 这里我们就直接使用官方推荐的Vue CLI方式 CLI (@vue/cli) 是一个全局安装的 npm 包,提供了终端里的 vue 命令.它可以通过 vue create 快速创建一个新项目 ...
- 关于微信登录授权获取unionid的方法
前言:微信登录授权是目前普遍存在于小程序的,还有一种静默授权方式是微信提供的但是不推荐使用,由于不同设备登录openid是不同的那么我们应该怎样拿到一个唯一的ID呢,下面做分享 wxml代码 < ...
- DirectX11--HLSL中矩阵的内存布局和mul函数探讨
前言 说实话,我感觉这是一个大坑,不知道为什么要设计成这样混乱的形式. 在我用的时候,以row_major矩阵,并且mul函数以向量左乘矩阵的形式来绘制时的确能够正常显示,并不会有什么感觉.但是也有人 ...
- NOI-OJ 1.13 ID:5 素数回文数的个数
整体思路 使用埃拉拖色尼算法提前计算出1000以内的素数表. 设计一个回文判断函数isHW(int n),难点在于数字回文的判断.一个简单的方法是将数字n使用sprintf输出在一个数组中,然后从数组 ...
- 第十节:委托和事件(2)(泛型委托、Func和Action、事件及与委托的比较)
一. 泛型委托 所谓的泛型委托,即自定义委托的参数可以用泛型约束,同时内置委托Func和Action本身就是泛型委托. 将上一个章节中的Calculator类中的方法用自定义泛型委托重新实现一下. p ...
- latex beamer 添加页码
导言中加 \setbeamertemplate{footline}[frame number]
- [物理学与PDEs]第2章习题10 一维理想流体力学方程组的 Lagrange 形式
试证明: 一维理想流体力学方程组的 Lagrange 形式 (5. 22)-(5. 24) 也可写成如下形式 $$\beex \bea \cfrac{\p \tau}{\p t}-\cfrac{\p ...