题意

给你一个长为 \(n\) 的环,你可以把它断成任意 \(k\) 段 \((1 < k \le n)\) ,使得每一段的 \(\gcd\) 都 \(>1\) 。

问总共有多少种方案,对于 \(10^9 + 7\) 取模。

\(n \le 10^5, 2 \le a_i \le 10^9\) 。

题解

首先我们考虑序列上怎么做。

我们令 \(dp_i\) 为到 \(i\) 这个点的方案数, \(pre_i\) 为 \(i\) 向前延伸最长的那个点满足 \((\displaystyle \gcd_{j = pre_i}^{i} a[j]) > 1\) 。

那么

\[\displaystyle dp_i = \sum_{j=pre_i-1}^{i-1} dp_j + [pre_i = 1]
\]

显然这个 \(dp\) 可以用前缀和来进行优化成 \(O(n)\) 的。

至于 \(pre_i\) 的处理可以用线段树求区间 \(\gcd\) ,然后用 two-pointers 来扫端点就行了,是 \(O(n( \log n + \log V))\) 的( \(\displaystyle V = \max_{i=1}^{n} a_i\) )。

好像利用 \(\gcd\) 的一些奇怪势能分析可以证明。


然后如果成环的话,我们只需要多考虑一种情况也就是 \(1,n\) 相连。

对于这种情况,可以考虑枚举最后面有 \(k\) 个数和 \(n\) 相连就行了。

然后每次计算的时候,可以类似于前面 \(dp\) 的计算就行了,但是要注意一下,那个 \([pre_i = 1]\) 要转化成后面 \(k\) 个数与前缀的 \(\gcd\) 是否 \(>1\) 。(也就是我们强行把后 \(k\) 个数当做一个整体提到前面就行了)

然后这样直接做是 \((n^2 \log V)\) 的,不够优秀。

但是我们发现很多数其实没有什么本质区别的,也就是后缀 \(\gcd\) 相同的一部分点可以一起计算。

这样的话,我们可以只在 \(\gcd\) 转折处,以及 \(0\) 号点计算就行了(因为要考虑上 \(1\) 向后那一片 \(\gcd\) 相同的数)。然后复杂度就是 \(O(n \log^2 V)\) 的了。

因为一个点向一端不断延伸,它的 \(\gcd\) 变换次数是 \(O(\log V)\) 的,因为每次变化至少会对于其中一个指数 \(-1\) 。

还有个特殊情况,也就是全部 \(\gcd > 1\) 的情况,需要将方案数 \(- (n - 1)\) 。(因为至少要分成 \(1\) 段)

最后就是 \(O(n (\log n + \log V) \log V)\) 的。

总结

对于一类划分环计数的题目,可以考虑枚举最后面那一段和前面相连的长度,然后直接一遍 \(O(n)\) 计数。

对于有些关于 \(\gcd\) 的题可以利用 \(\gcd\) 变换次数不超过 \(O(\log V)\) 来做。

代码

代码还是很好写的,可以参考一下。(其实博主参考了一下 ysgs 大佬的代码 )

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << x << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
return x * fh;
} void File() {
#ifdef zjp_shadow
freopen ("gcd-on-a-circle.in", "r", stdin);
freopen ("gcd-on-a-circle.out", "w", stdout);
#endif
} #define lson o << 1, l, mid
#define rson o << 1 | 1, mid + 1, r const int N = 1e5 + 1e3, Mod = 1e9 + 7; int a[N];
struct Segment_Tree { int Gcd[N << 2]; void Build(int o, int l, int r) {
if (l == r) { Gcd[o] = a[l]; return ; }
int mid = (l + r) >> 1;
Build(lson); Build(rson); Gcd[o] = __gcd(Gcd[o << 1], Gcd[o << 1 | 1]);
} int Query(int o, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return Gcd[o];
int mid = (l + r) >> 1;
if (qr <= mid) return Query(lson, ql, qr);
if (ql > mid) return Query(rson, ql, qr);
return __gcd(Query(lson, ql, qr), Query(rson, ql, qr));
} } T; inline int Add(int a, int b) { return (a += b) >= Mod ? a - Mod : a; } int n, sum[N], dp[N], pre[N]; void Calc(int cur) {
sum[0] = 1;
For (i, 1, n) {
cur = __gcd(cur, a[i]);
if (cur > 1) dp[i] = sum[i - 1];
else dp[i] = Add(sum[i - 1], Mod - sum[max(0, pre[i] - 2)]);
sum[i] = Add(sum[i - 1], dp[i]);
}
} int main () { File(); n = read();
For (i, 1, n) a[i] = read(); T.Build(1, 1, n); pre[1] = 1;
For (i, 2, n)
for (pre[i] = pre[i - 1]; pre[i] < i; ++ pre[i])
if (T.Query(1, 1, n, pre[i], i) > 1) break; int Last = n, suf = 0, cur, ans = 0;
Fordown (i, n, 1) {
cur = __gcd(a[i], suf);
if (cur != suf || i == 1) {
Calc(suf); ans = Add(ans, Add(sum[Last], Mod - sum[i - 1]));
Last = i - 1; suf = cur;
}
} printf ("%d\n", Add(ans, (Mod - (T.Gcd[1] > 1 ? (n - 1) : 0)))); return 0;
}

CS Academy Gcd on a Circle(dp + 线段树)的更多相关文章

  1. ZOJ 3349 Special Subsequence 简单DP + 线段树

    同 HDU 2836 只不过改成了求最长子串. DP+线段树单点修改+区间查最值. #include <cstdio> #include <cstring> #include ...

  2. hdu 3016 dp+线段树

    Man Down Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total S ...

  3. cf834D(dp+线段树区间最值,区间更新)

    题目链接: http://codeforces.com/contest/834/problem/D 题意: 每个数字代表一种颜色, 一个区间的美丽度为其中颜色的种数, 给出一个有 n 个元素的数组, ...

  4. Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树)

    Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树) 题目链接 题意 给定一个nm的矩阵,每行取2k的矩阵,求总 ...

  5. POJ1769 Minimizing maximizer(DP + 线段树)

    题目大概就是要,给一个由若干区间[Si,Ti]组成的序列,求最小长度的子序列,使这个子序列覆盖1到n这n个点. dp[i]表示从第0个到第i个区间且使用第i个区间,覆盖1到Ti所需的最少长度 对于Si ...

  6. [USACO2005][POJ3171]Cleaning Shifts(DP+线段树优化)

    题目:http://poj.org/problem?id=3171 题意:给你n个区间[a,b],每个区间都有一个费用c,要你用最小的费用覆盖区间[M,E] 分析:经典的区间覆盖问题,百度可以搜到这个 ...

  7. POJ 3162 Walking Race 树形DP+线段树

    给出一棵树,编号为1~n,给出数m 漂亮mm连续n天锻炼身体,每天会以节点i为起点,走到离i最远距离的节点 走了n天之后,mm想到知道自己这n天的锻炼效果 于是mm把这n天每一天走的距离记录在一起,成 ...

  8. 【uva1502/hdu4117-GRE Words】DP+线段树优化+AC自动机

    这题我的代码在hdu上AC,在uva上WA. 题意:按顺序输入n个串以及它的权值di,要求在其中选取一些串,前一个必须是后一个的子串.问d值的和最大是多少. (1≤n≤2×10^4 ,串的总长度< ...

  9. bzoj 1835 [ZJOI2010]base 基站选址(DP+线段树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1835 [题意] 有n个村庄,每个村庄位于d[i],要求建立不多于k个基站,在第i个村庄 ...

随机推荐

  1. SQLSERVER事务日志已满 the transaction log for database 'xx' is full

    解决办法:清除日志 USE [master] GO ALTER DATABASE DNName SET RECOVERY SIMPLE WITH NO_WAIT GO ALTER DATABASE D ...

  2. 帮助小白,最新版JDK的安装与环境变量配置(Win 10系统)

    学习JAVA,必须首先安装一下JDK(java development kit java开发工具包),之后再配置环境变量就可以开始使用JAVA了. 一,安装JDK 1,可以选择到官网下载最新版本的JD ...

  3. PhpStorm的注册激活方法

    首先,需要修改本地的hosts文件(路径一般为C:\Windows\System32\drivers\etc\hosts),添加下面这行代码. 0.0.0.0 account.jetbrains.co ...

  4. 【kindle笔记】之 《明朝那些事儿》-2018-7-1

    [kindle笔记]读书记录-总 最近在读这本书.之前在微信读书里断断续续读过,读到深处还想蹦起来做笔记那种.后来种种原因断了,再没续上. 现在又开始啦.最近还在重八兄造反阶段,还很早呢,有时候晚上玩 ...

  5. tomcat one connection one thread one request one thread

    java - What is the difference between thread per connection vs thread per request? - Stack Overflow ...

  6. myecplise ssh项目配置上遇到的问题

    版本:spring3.1+hib4.1+struts2.1 学习项目使用此版本运行时,总是会遇到各样的错误,在这里做一下记录. 问题1:log4j相关 spring的web项目,执行时报错: 信息: ...

  7. 通过爬虫程序深入浅出java 主从工作模型

    随手做的爬虫程序在   https://github.com/rli07/master_java/blob/master/spider.zip  可下载. 这是我做的系统学习图, 可以参考一下 系统架 ...

  8. 爱上linux 简单实现移动办公处理环境.

    1. 这周一直在鼓捣linux上面的环境测试. 简单的将 我们的产品部署到了linux上面 详情见前面的 blog 2. 有时候下班了 或者是 在WC (科技园wc排队 说多了都是泪) 或者是眼睛不舒 ...

  9. LLVM的安装

    1. 官网下载 llvm 2. 官网下载cmake 3. configure 执行 llvm 发现报错 4. 解压缩 cmake 5.将cmake 下面的bin 目录放到环境变量里面去 6. 创建一个 ...

  10. Docker部署运行springboot项目,并使用Dockerfile制作镜像

    前言: 本来是要搭建一个自动化部署分布式项目的服务器平台的,使用jenkins+k8s+ELK+springboot把一个简单的springboot项目给搞起来,由于工程太大,先分开把每个技术组件单独 ...