P7810 [JRKSJ R2] Upper
题意
给长为 \(n\) 的序列 \(a\),要求划分为若干段,使得段内左右端点 \(l,r\) 有 \(a[l] < a[r], \gcd(a[l], a[r]) > 1\)。 \(n \le 10^5, a \le 10^9\)。
分析
首先由线性dp。
\(f[i] = \max\limits_{p | a[i]}\max\limits_{j < i, a[j] < a[i],p | a[j]} f[j-1]\)。
这样转化为一个二维偏序问题。
然后我们暴力对每个出现过的质因数维护一个动态开点权值线段树。支持前缀 \(\max\) 和单点修改。
空间复杂度 \(\mathcal{O} (n\omega(n)\log n)\),算出来 171 MB。相当稳。
这里有个小技巧,先晒出 \(\sqrt a\) 的素数,每次用素数试除,这样复杂度是 \(\mathcal{O} (\dfrac{\sqrt a}{\log a})\)。
还有就是空间复杂度有时按照使用的来算,这样就不要写构造函数了。
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef double db;
#define fin(h) freopen(#h".in","r",stdin);
#define fout(h) freopen(#h".out","w",stdout);
inline int read() {
int x=0,v=1,ch=getchar();
while('0'>ch||ch>'9') {
if(ch=='-')v=0;
ch=getchar();
}while('0'<=ch&&ch<='9') {
x=(x*10)+(ch^'0');
ch=getchar();
}return v ? x : -x;
}
const int MAX=1e5+5, V = 40000+5, INF = 0x3f3f3f3f;
int n, a[MAX];
vector<int>G[MAX];
int p[V], vis[V];
inline void init(int N = 40000) {
vis[1] = 1;
for(int i=2;i<=N;++i) {
if(!vis[i]) p[++p[0]] = i;
for(int j=1;j<=p[0] && i*p[j]<=N;++j) {
vis[i * p[j]] = p[j];
if(i % p[j] == 0) break;
}
}
}
inline void rho(int j) {
int x = a[j];
for(int i=1;p[i]*p[i]<=x;++i) {
if(x % p[i] == 0) {
G[j].emplace_back(p[i]);
while(x % p[i] == 0) x/=p[i];
}
} if(x > 1) G[j].emplace_back(x);
}
const int HAHA = MAX;
unordered_map<int,int>HA;
struct seg{
int rt[MAX];
struct node {
int l,r,v;
}tr[MAX*153];
int tot;
inline void ps(int x) { tr[x].v = max(tr[tr[x].l].v, tr[tr[x].r].v); }
void mdf(int &x,int l,int r, int t, int val) {
if(!x) x = ++tot;
if(l==r) {
tr[x].v = max(tr[x].v, val);
return ;
} int mid=l+r>>1;
if(t<=mid) mdf(tr[x].l,l,mid,t,val);
else mdf(tr[x].r,mid+1,r,t,val);
ps(x);
}
int ask(int x,int l,int r,int s,int t){
if(!x) return -INF;
if(s<=l&&r<=t) return tr[x].v;
int mid=l+r>>1, ret=-INF;
if(s<=mid) ret = max(ret, ask(tr[x].l,l,mid,s,t));
if(mid<t) ret = max(ret, ask(tr[x].r,mid+1,r,s,t));
return ret;
}
}T;
int f[MAX];
signed main() {
init();
n=read();
for(int i=1;i<=n;++i) {
a[i] = read();
rho(i);
} int cnt = 0;
f[0] = 0;
for(int i=1;i<=n;++i) {
f[i] = -INF;
for(int v : G[i]) {
if(!HA.count(v)) HA[v] = ++ cnt, T.rt[cnt] = ++T.tot;
int t = HA[v];
f[i] = max(f[i], T.ask(T.rt[t],1, INF, 1, a[i] - 1) + 1);
}
if(f[i-1] >= 0)
for(int v : G[i]) {
int t = HA[v];
T.mdf(T.rt[t], 1, INF , a[i], f[i-1]);
}
}
printf("%lld\n",f[n] < 0? -1 : f[n]);
return 0;
}