AT2651 [ARC077D] SS
定义 \(nxt_i\) 表示在字符串 \(S\) 中以 \(i\) 结尾的最长 \(border\)。
引理一:若 \(n - nxt_n \mid n\) 则 \(S_{1 \sim n - nxt_n}\) 为原串的最小循环节。
引理二:若 \(n - nxt_n \nmid n\) 则 \(S_{1 \sim n - nxt_n}\) 为原串的弱循环节(即原串可以 \(S_{1 \sim n - nxt_n}\) 不断循环但最终一段不能完整循环完毕)
关于引理一的 证明,引理二类似。
为了方便,下面我们令 \(g(S) = S_{1 \sim n - nxt_n} = T\)。
观察一下 \(f\) 的变化规则可以发现,第一次我们一定会找到 \(S\) 的最长 \(border\) 然后在后面接上 \(SS\) 除了前后两个 \(border\) 的中间部分,即 \(f(SS) = STST\)。
因为生成的 \(f\) 依然是偶串,那么前后串必然相同,经过无限次迭代后我们只需考虑前半部分即 \(S\) 演变规律即可。
因为 \(f(S) = Sg(S)\) 不难发现难点在于如何找到每次迭代后的 \(g(S)\)。
基于观察可以发现以下两个性质:
若 \(|g(S)| \mid |S|\) 则 \(g(f(S)) = g(S)\)
否则 \(g(f(S)) = S\)
性质一的证明是显然的,下面考虑如何证明性质二。
根据引理二,只需证明 \(T\) 为 \(f(S)\) 的最长 \(border\) 即可。
假设存在一个更长的 \(border X, |X| > |T|\),首先假设 \(|T| \nmid |X|\)。
可以发现 \(X \% T\)(\(X\) 比上一个 \(T\) 多出来的部分) 会是 \(T\) 的一个弱循环节,若此时 \(|X \% T| \mid |T|\) 则 \(S\) 存在一个更短的弱循环节 \(X \% T\)。
否则,以原来 \(S\) 的视角观察 \(T\) 可以发现 \(T \% (X \% T)\) 会是 \(X \% T\) 的的一个弱循环串。
注意到其本质是对于一个原串 \(b\) 其弱循环节 \(a\) 必然能推出 \(b \% a\) 为 \(a\) 的弱循环节,本质上即 \((a, b) \rightarrow (b \% a, a)\) 这就是辗转相除的过程。
所以我们必然可以推出 \(\gcd(X \% T, T)\) 会是原串的弱/真循环节其长度小于 \(T\),矛盾。
对于 \(|T| \mid |X|\) 的从 \(border\) 处以类似的方式也可推出矛盾。
这样一来,就可以发现 \(f\) 进过无限次迭代之后的规律了。
若 \(|T| \mid |S|\) 则 \(f ^ \infty (S) = STTTTT\cdots\)
若 \(|T| \nmid |S|\) 则 \(f ^ i (S) = f ^ {i - 1} (S) + f ^ {i - 2} (S)\)
对于第一种情况,直接计算即可。
对于第二种情况,首先差分转化为求 \([1, r]\) 中字符的出现次数。因为斐波那契的增长速度是指数级的,因此我们可以暴力计算出 \(f\) 在 \(1e18\) 长度内每个值的字符出现个数;最终直接使用递归的方式往下求解即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int N = 2e5 + 5;
const int M = 30 + 5;
char s[N]; int n, l, r, nxt[N], ans[N];
int read() {
char c; int x = 0, f = 1;
c = getchar();
while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
namespace S1 {
void solve(int L, int buf) {
rep(i, 1, n - nxt[n]) ans[s[i] - 'a'] += buf * (max(0ll, L - n) / (n - nxt[n]));
rep(i, 1, min(L, n)) ans[s[i] - 'a'] += buf;
if(L > n) rep(i, 1, L % (n - nxt[n])) ans[s[i] - 'a'] += buf;
}
}
namespace S2 {
int g[N], f[N][M];
void dfs(int p, int L, int buf) {
if(!L) return ;
if(p == 0) { rep(i, 1, L) ans[s[i] - 'a'] += buf; return ; }
if(p == 1) {
rep(i, 1, min(L, n)) ans[s[i] - 'a'] += buf;
rep(i, 1, L - n) ans[s[i] - 'a'] += buf;
return ;
}
if(g[p - 1] <= L) {
rep(i, 0, 25) ans[i] += f[p - 1][i] * buf;
dfs(p - 2, L - g[p - 1], buf);
}
else dfs(p - 1, L, buf);
}
void solve(int L, int buf) {
memset(f, 0, sizeof(f)), memset(g, 0, sizeof(g));
rep(i, 1, n) f[0][s[i] - 'a']++, f[1][s[i] - 'a']++;
rep(i, 1, n - nxt[n]) f[1][s[i] - 'a']++;
g[0] = n, g[1] = n * 2 - nxt[n];
for (int i = 0; ; ++i) {
if(i > 1) {
g[i] = g[i - 1] + g[i - 2];
rep(j, 0, 25) f[i][j] = f[i - 1][j] + f[i - 2][j];
}
if(g[i] > L) { dfs(i, L, buf); break;}
}
}
}
signed main() {
scanf("%s", s + 1), n = strlen(s + 1) / 2;
l = read(), r = read();
for (int i = 2, j = 0; i <= n; ++i) {
for (; s[i] != s[j + 1] && j; j = nxt[j]) ;
nxt[i] = ((s[i] == s[j + 1]) ? (++j) : j);
}
if(n % (n - nxt[n]) == 0) {
S1 :: solve(r, 1), S1 :: solve(l - 1, -1);
rep(i, 0, 25) printf("%lld ", ans[i]);
}
else {
S2 :: solve(r, 1), S2 :: solve(l - 1, -1);
rep(i, 0, 25) printf("%lld ", ans[i]);
}
return 0;
}
像这类无限次迭代的题目一定要寻找规律,将规律用某些学习过的变量表示,这样可以方便下面的推导。
注意文中提到的证明方法,可能可以解决一部分字符串循环节问题。
AT2651 [ARC077D] SS的更多相关文章
- 极路由2(极贰)在OpenWrt下定制自己的ss服务
默认刷入的OpenWrt带的ss, 只有ss-redir服务, 但是在实际使用中, 很多时候还是希望访问直接通过正常网关, 只有少部分访问需要通过ss, 所以希望能配置成为ss-local服务. 在保 ...
- 记一次ss故障
本文主要参考: https://github.com/shadowsocks/shadowsocks shadowssocks 分为客户端和服务器端. 我们平时买的服务,使用是要用的是客户端. 如果你 ...
- ss命令和Recv-Q和Send-Q状态
ss 用来显示处于活动状态的套接字信息.ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容.但ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信息,而且比nets ...
- SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
java日期格式大全 format SimpleDateFormat(转) SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH ...
- SS - DIY一个前端模板引擎.(一)
前端MVVM 模式有点很多,完全摆脱了意大利面条式的代码. 个人认为,所有MVVM 的框架基础就是一个高性能的JS模板引擎,它极大简化了 DOM 操作, 使页面渲染和业务逻辑彻底分离. 为了理解模板引 ...
- YYYY-mm-dd HH:MM:SS
备忘:YYYY-mm-dd HH:MM:SS部分解释 d 月中的某一天.一位数的日期没有前导零. dd 月中的某一天.一位数的日期有一个前导零 ...
- js 获取当前日期时间3种格式化方法 yyyy-mm-dd hh:MM:ss
方法一: Date.prototype.format = function (format) { var args = { "M+": this.getMonth() + 1, & ...
- 在VPS上搭建SS访问火星
前段时间发布了Visual Studio 2017 RC,由于现在VS没有离线的ISO了,只有一个在线安装文件.虽然可以通过这个在线安装文件生成完整的离线安装包(之前的ISO版本在安装过程中仍然需要联 ...
- 每天一个linux命令(57):ss命令
ss是Socket Statistics的缩写.顾名思义,ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容.但ss的优势在于它能够显示更多更详细的有关TCP和连接状态的 ...
随机推荐
- 第三十六个知识点:Index Calculus算法
第三十六个知识点:Index Calculus算法 我们这篇博客继续描述一种数学攻击,这种数学攻击被叫做Index Calculus(IC)算法. 注意这里Index Calculus算法没有找到合适 ...
- Geometric GAN
目录 概 主要内容 McGAN 结合SVM 训练 训练 理论分析 证明 Jae Hyun Lim, Jong Chul Ye, Geometric GAN. 概 很有趣, GAN的训练过程可以分成 寻 ...
- 实战!退出登录时如何借助外力使JWT令牌失效?
大家好,我是不才陈某~ 今天这篇文章介绍一下如何在修改密码.修改权限.注销等场景下使JWT失效. 文章的目录如下: 解决方案 JWT最大的一个优势在于它是无状态的,自身包含了认证鉴权所需要的所有信息, ...
- 登陆认证框架:SpringSecurity
最近想给自己的小系统搭建一个登录认证服务,最初是想着一套oauth2权鉴就可以,但是发现这个oauth2只是权鉴,具体的登录认证需要由 SpringSecurity来进行实现. 也就是说SpringS ...
- Java程序设计基础笔记 • 【第4章 条件结构】
全部章节 >>>> 本章目录 4.1 条件结构 4.1.1 程序流程控制 4.1.2 单分支if结构 4.1.3 双分支if结构 4.1.4 实践练习 4.2 多重条件结 ...
- Drools集成SpringBoot
1.说明 为了更好的在项目中使用Drools, 需要把Drools集成到Spring Boot, 下面介绍集成的方法, 并且开发简单的Demo和测试用例. 2.创建Maven工程 pom.xml工程信 ...
- java知识点链接
业务复杂=if else?刚来的大神竟然用策略+工厂彻底干掉了他们! 细思极恐-你真的会写java吗? [解锁新姿势] 兄dei,你代码需要优化了 消灭 Java 代码的"坏味道" ...
- InnoDB学习(八)之 聚簇索引
InnoDB中,表数据文件本身就是以主键为索引的B+树,树的叶子节点存放一条条表数据,此索引树被称为表的聚簇索引.聚簇索引也称为聚集索引,聚类索引,簇集索引,聚簇索引确定表中数据的物理顺序. Inno ...
- Kafka和RabbitMQ有哪些区别,各自适合什么场景?
经常有人问我 有个 xx 需求,我应该用 Kafka 还是 RabbitMQ ? 这个问题很常见,而且很多人对二者的选择也把握不好. 所以我决定写篇文章来详细说一下:Kafka 和 RabbitMQ ...
- Drupal 8 环境搭建部署
运行环境安装 系统:Ubuntu Server 16.04 (ubuntu-16.04.4-server-amd64.iso) Web服务器:Apache / 2.4.18 数据库:Mysql / ...