原文链接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++;elsem++,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;
}

  

转载于:https://www.cnblogs.com/zhouzhendong/p/UOJ172.html

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

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

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

  2. [WC2016] 论战捆竹竿 [学习笔记] 同余最短路

    本来想做字符串的题,结果发现了这个坑把他填上. 什么是同余最短路? 同余最短路是解决这样一类问题:给你若干个数 a [ i ] a[i] a[i] ,每个数可以选无限次加起来,问最后的和有多少种在 m ...

  3. BZOJ4406 WC2016 论战捆竹竿

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

  4. 【WC2016】论战捆竹竿

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

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

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

  6. Acwing算法基础课学习笔记(四)--数据结构之单链表双链表模拟栈模拟队列单调栈单调队列KMP

    单链表 算法题中最常考的单链表就是邻接表(用来存储图和数),比如最短路问题,最小生成树问题,最大流问题.双链表用于优化某些问题. 利用数组来表达单链表:存储值和指针的两个数组利用下标进行关联. 需要注 ...

  7. DSt:数据结构的最强学习路线之数据结构知识讲解与刷题平台、刷题集合、问题为导向的十大类刷题算法(数组和字符串、栈和队列、二叉树、堆实现、图、哈希表、排序和搜索、动态规划/回溯法/递归/贪心/分治)总

    DSt:数据结构的最强学习路线之数据结构知识讲解与刷题平台.刷题集合.问题为导向的十大类刷题算法(数组和字符串.栈和队列.二叉树.堆实现.图.哈希表.排序和搜索.动态规划/回溯法/递归/贪心/分治)总 ...

  8. [单调栈/差分/尺取/单调队列]Exercise Week5 A最大矩形+B魔法猫+C平衡字符串+D滑动窗口

    目录 A.[单调栈]最大矩形 题意 样例 思路 总结 代码 B.[差分]TT's Magic Cat 题意 样例 思路 总结 代码 C.[尺取]平衡字符串 题意 样例 思路 总结 代码 D.[单调队列 ...

  9. 流动python - 字符串KMP匹配

    首先我们看一下简单的字符串匹配. 你可以把文本字符串s固定,模式字符串p从s对齐的左边缘,作为承担部分完全一致,匹配成功,失败将是模式字符串p整体向右1地点,继续检查对齐部分,重复. #朴素匹配 de ...

最新文章

  1. 逐步创建Data Guard
  2. linux 磁盘分区情况6,Linux磁盘分区介绍
  3. STL--lower_bound()upper_bound();
  4. 台湾一公司因停电,让员工手写代码...
  5. 关于Google Video
  6. idea右键单击没有 svn选项处理办法
  7. net修复工具_Stimulsoft Ultimate v2020.3.2修复性更新升级!| 附下载
  8. c语言代码re通常什么错误,C语言,realloc动态内存申请,出现报错double free or corruption (!prev)...
  9. javascript实现计算器
  10. Tableau bug合集1:无法展示emoji表情包
  11. 【软件安装】CuteFTP Pro安装过程(含安装包)
  12. Python爬虫案例——漫画网站爬取图片(模拟浏览器解析网页)
  13. plot confusion_matrix
  14. CUDA11.4+VS2017+MATLAB2020a执行mexcuda遇到的问题及解决
  15. 查看linux是多少位
  16. 好记性不如烂笔头——Vite篇
  17. 【精选实践】爱奇艺实用数据库选型树:不同场景如何快速选择数据库?
  18. 求弹性模量和泊松比计算题_弹性模量越大说明什么?弹性模量和泊松比
  19. 版本控制工具Git(完美整理版)
  20. 基于横向轨迹误差法(Cross-track Error)P 导航二维控制 实现无人机水平面导航控制

热门文章

  1. Zabbix 5.0通过SNMPv2监控交换机
  2. 1078:求分数序列和
  3. 数学建模–模拟退火算法
  4. 如何修改Win11上的默认程序?
  5. 入耳式蓝牙耳机哪个好?平价蓝牙耳机品牌
  6. 怎样保存python源程序_Python编程第1课,认识IDLE,编写保存运行第1个程序
  7. python|教你一行代码用Ta的名字比心
  8. c语言 fgets用法,fgets和fputs的用法详解
  9. 中国金控巴彦淖尔签约-国稻种芯·林裕豪:从玉农业谋定塞外
  10. 解决homebrew install卡住问题