「WC2016」论战捆竹竿
「WC2016」论战捆竹竿
前置知识
参考资料:《论战捆竹竿解题报告—王鉴浩》,《字符串算法选讲—金策》。
Border&Period
若前缀 \(pre(s,x)\) 与后缀 \(suf(s,n-x-1)\) 相等,则 \(pre(s, x)\) 是 \(s\) 的一个 \(\text{Border}\)。
\(x\) 是 \(s\) 的一个周期 (\(\text{Preiod}\)) 满足 \(s[i]=s[i+x],\forall{1\leq i\leq|s|-x}\) 。
对于一个 \(\text{Border } pre(s, x)\) ,满足 \(|s|-x\) 是 \(s\) 的一个周期。
若 \(s\) 最长的 \(Boder\) 为 \(pre(s, x)\) ,则 \(s\) 的 \(\text{Border}\) 数量为 \(pre(s, x)\) 的 \(\text{Border}\) 数量 \(+1\) 。
Periodicity Lemma
若 \(p, q\) 是 \(s\) 的周期,且满足 \(p+q+\gcd(p,q)\leq |s|\) ,则 \(gcd(p,q)\) 也是 \(s\) 的一个周期。
Borders 的性质
引理:对于长度 \(\geq\) \(\frac{|s|}{2}\) 的 \(s\) 的 \(\text{Border}\) ,其长度组成一个等差数列。
证明:假设有满足条件的 \(\text{Boder}\) 集合 \(a,\forall a_i \geq \frac{|s|}{2}\) ,可以得到周期集合 \(b, \forall b_i\leq \frac{|s|}{2}\) 。
根据 Periodicity Lemma,\(b\) 的所有元素的 \(\gcd\) 也是 \(s\) 的一个周期,显然 \(a\) 集合能组成公差为 \(g\) 的等差数列。
推论:字符串 \(s\) 的所有 $\text{Border} $ 可以分成 \(O(\log|S|)\) 段等差数列。
根据引理, \(s\) 所有长度 \(\geq \frac{|s|}{2}\) 的 \(\text{Border}\) 可以分成一个等差数列 ,设 \(s\) 最长的不超过 \(\frac{|s|}{2}\) 的 \(\text{Border}\) 为 \(pre(s, m)\) ,记 \(T(n)\) 为 \(pre(s, n)\) 的 \(\text{Border}\) 分成的等差数列数量,显然有
\]
同理,字符串所有周期也可以被分成 \(O(\log|S|)\) 段等差数列。
解题思路
先转化一下题意,一开始字符串长度为 \(n\) ,每次可以接上初始字符串的一个周期或者初始字符串本身,求在 \(w\) 范围内能得到的字符串的长度种类数。
先不考虑 Border&Period 的性质,可以直接转化为一个模 \(n\) 意义下的最短路问题,复杂度 \(O(n^2)\) 。
由 前置知识 可以得知,每次加的长度可以分成 \(O(\log n)\) 段等差数列,考虑对等差数列内部怎么快速计算。
对于一个项数为 \(m\) ,首项为 \(v\) ,公差为 \(d\) 的等差数列,把转移放到模 \(v\) 意义下做,每次转移就相当与加若干个 \(d\) ,并且考虑转移是若干个互不相交的环,对于一个环可以发现,当前环内部最小的一位肯定不会被更新。所以可以从这一位开始依次更新整个环。
单独考虑一个环,令 \(i,j\) 为展开后环上第 \(i\) 个元素和第 \(j\) 个元素,\(j\) 能更新 \(i\) 当且仅当 \(j < i\) 且 \(i-j < m\) ,这个东西可以直接用单调队列维护。
接下来考虑将两段等差数列的贡献合并,只需要将维护的东西从模一个首项意义转移到另外一个即可,那么只需要对应的位置更新完后,再做一遍长度为之前首项的转移即可,这样子就大概做完了,总复杂度 \(\mathcal O(n\log n)\) 。
code
/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf ((ll)0x3f3f3f3f3f3f3f3f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
const int N = 500005;
char s[N];
int a[30][N];
ll dp[N], q[N], g[N], w, ans;
int b[N], id[N], vis[N], nxt[N], n, tot;
inline void gao(int *a, int len, int lst){
int v = a[1], d = len > 1 ? a[2] - a[1] : 1;
for(int i = 0; i < v; i++) g[i] = inf, vis[i] = 0;
for(int i = 0; i < lst; i++) g[dp[i]%v] = Min(g[dp[i]%v], dp[i]);
for(int i = 0; i < v; i++) dp[i] = g[i];
for(int i = 0; i < v; i++) if(!vis[i]){
ll mn = dp[i]; int pos = i; vis[i] = 1;
for(int j = (i + lst) % v; j != i; j = (j + lst) % v){
if(dp[j] < mn) mn = dp[j], pos = j;
vis[j] = 1;
}
dp[(pos+lst)%v] = Min(dp[(pos+lst)%v], dp[pos] + lst);
for(int j = (pos + lst) % v; j != pos; j = (j + lst) % v)
dp[(j+lst)%v] = Min(dp[(j+lst)%v], dp[j] + lst);
}
if(len == 1) return;
for(int i = 0; i < v; i++) vis[i] = 0;
for(int i = 0; i < v; i++) if(!vis[i]){
ll mn = dp[i]; int pos = i; vis[i] = 1;
for(int j = (i + d) % v; j != i; j = (j + d) % v){
if(dp[j] < mn) mn = dp[j], pos = j;
vis[j] = 1;
}
int h = 1, t = 1; q[1] = dp[pos], id[1] = 0;
for(int k = 1, j = (pos + d) % v; j != pos; j = (j + d) % v, ++k){
while(h <= t && k - id[h] >= len) ++h;
if(h <= t) dp[j] = Min(dp[j], q[h] + v + (k - id[h]) * d);
while(h <= t && q[t] - id[t] * d > dp[j] - k * d) --t;
q[++t] = dp[j], id[t] = k;
}
}
}
inline void solve(){
read(n), read(w); tot = 0;
scanf("%s", s + 1);
for(int i = 2, j = 0; i <= n; nxt[i++] = j){
while(j && s[j+1] != s[i]) j = nxt[j];
if(s[j+1] == s[i]) j++;
}
for(int x = nxt[n]; x; x = nxt[x]){
int flag = 0;
for(int i = 1; i <= tot; i++)
if(b[i] == 1 || a[i][2] - a[i][1] == (n - x) - a[i][b[i]]){
a[i][++b[i]] = n - x, flag = 1; break;
}
if(!flag) ++tot, a[tot][b[tot]=1] = n - x;
}
dp[0] = n;
for(int i = 1; i < n; i++) dp[i] = inf;
int lst = n;
for(int i = 1; i <= tot; i++)
gao(a[i], b[i], lst), lst = a[i][1];
ans = 0;
for(int i = 0; i < lst; i++) if(dp[i] <= w) ans += (w - dp[i]) / lst + 1;
cout << ans << endl;
}
int main(){
int T; read(T); while(T--) solve();
return 0;
}
「WC2016」论战捆竹竿的更多相关文章
- UOJ#172. 【WC2016】论战捆竹竿 字符串 KMP 动态规划 单调队列 背包
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ172.html 题解 首先,这个问题显然是个背包问题. 然后,可以证明:一个字符串的 border 长度可 ...
- UOJ#172. 【WC2016】论战捆竹竿
传送门 首先这个题目显然就是先求出所有的 \(border\),问题转化成一个可行性背包的问题 一个方法就是同余类最短路,裸跑 \(30\) 分,加优化 \(50\) 分 首先有个性质 \(borde ...
- bzoj4406: [Wc2016]论战捆竹竿&&uoj#172. 【WC2016】论战捆竹竿
第二次在bzoj跑进前十竟然是因为在UOJ卡常致死 首先这个题其实就是一个无限背包 一般做法是同余最短路,就是bzoj2118: 墨墨的等式可以拿到30分的好成绩 背包是个卷积就分治FFT优化那么下面 ...
- 【WC2016】论战捆竹竿
已经快三周了啊--终于把挖的坑填了-- 首先显然是把除了自身的所有border拿出来,即做 \(\left\{ n - b_1, n - b_2, \dots, n - b_k, n \right\} ...
- 「WC2016」挑战NPC
「WC2016」挑战NPC 解题思路 这个题建图非常厉害,带花树什么的只会口胡根本写不动,所以我写了机房某大佬教我的乱搞. 考虑把一个筐 \(x\) 拆成 \(x1,x2,x3\) 三个点,且这三个点 ...
- luogu P4156 [WC2016]论战捆竹竿
传送门 官方题解(证明都在这) 神仙题鸭qwq 转化模型,发现这题本质就是一个集合,每次可以加上集合里的数,问可以拼出多少不同的数 首先暴力需要膜意义下的最短路,例题戳这 然后这个暴力可以优化成N^2 ...
- Luogu4156 WC2016 论战捆竹竿 KMP、同余类最短路、背包、单调队列
传送门 豪华升级版同余类最短路-- 官方题解 主要写几个小trick: \(1.O(nm)\)实现同余类最短路: 设某一条边长度为\(x\),那么我们选择一个点,在同余类上不断跳\(x\),可以形成一 ...
- BZOJ4406 WC2016 论战捆竹竿
Problem BZOJ Solution 显然是一个同余系最短路问题,转移方案就是所有|S|-border的长度,有 \(O(n)\) 种,暴力跑dijkstra的复杂度为 \(O(n^2\log ...
- 【LuoguP4156】论战捆竹竿
题目链接 题意简述 你有一个长度为 n 的字符串 , 将它复制任意次 , 复制出的串的前缀可以与之前的串的后缀重叠在一起 , 问最后总共可能的长度数目 , 长度不能超过 \(w\) 多组数据. \(n ...
随机推荐
- jQuery中一些不常用的方法属性【转载】
index(subject) 搜索与参数表示的对象匹配的元素,并返回相应元素的索引值.如果找到了匹配的元素,从0开始返回:如果没有找到匹配的元素,返回-1. data() data(elem):为页面 ...
- 【洛谷 P3199】 [HNOI2009]最小圈(分数规划,Spfa)
题目链接 一开始不理解为什么不能直接用\(Tarjan\)跑出换直接求出最小值,然后想到了"简单环",恍然大悟. 二分答案,把所有边都减去\(mid\),判是否存在负环,存在就\( ...
- 海洋CMS v6.53 v6.54命令执行
测试下载地址:https://pan.baidu.com/s/1jHQBKFk 至于分析实在是看的一脸懵逼就不累赘了.直接上exp POST /haiyang/upload/search.php HT ...
- css3动画详解
一.Keyframes介绍: Keyframes被称为关键帧,其类似于Flash中的关键帧.在CSS3中其主要以“@keyframes”开头,后面紧跟着是动画名称加上一对花括号“{…}”,括号中就是一 ...
- MVC开发模式与javaEE三层架构
1.MVC开发模式 1. M:Model,模型.JavaBean * 完成具体的业务操作,如:查询数据库,封装对象2. V:View,视图.JSP * 展示数据3. C:C ...
- Java容器---基本概念
1.持有对象 Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念: (1) Collection: 一个独立元素的序列,这些元素都服从一条或多条规则.List必须按照插入的顺序保存元素 ...
- LeetCode818. Race Car
https://leetcode.com/problems/race-car/description/ Your car starts at position 0 and speed +1 on an ...
- Mysql查询语句的运行流程
我们先看一下MYsql的基本架构示意图: 大体来说,MySQL 可以分为 Server 层和存储引擎层两部分. Server 层包括连接器.查询缓存.分析器.优化器.执行器等,涵盖 MySQL 的大多 ...
- 重记解决kube-dns故障一则---ceph惹的祸
上次,在同一个k8s集群里安装完ceph进行功能测试. 当测试完成之后,我停止了ceph的程序,再重新启动k8s集群. 结果,有一个应用就出问题了. 后来查出是因为防火墙里 Chain FORWARD ...
- 尝试 TFS Express 2012.3
之前一直使用SVN做版本管理,但是只能管理代码.之前的一份工作,只用了TFS来管理,可以将任务与代码集成管理,很是方便,只是安装太过于繁琐,现在的公司人少,不想费那么多事. 最关键的,就是安装TFS需 ...