Codeforces Round #717 (Div. 2) D. Cut (质数分解,倍增dp)
-
题意:给你\(n\)个数,有\(q\)个询问,每次询问一个区间,问你这个区间至少要分成多少个子区间,使得每个子区间的所有元素乘积等于它们的\(lcm\).
-
题解:因为\(lcm(x,y)=\frac{x*y}{gcd(x,y)}\),推广一下不难发现,要满足题目条件的话,区间内所有元素的\(gcd=1\).即不能有公共因子.我们可以倒着遍历,维护每个数质因子的最小位置,然后当遍历到下一个数的时候,我们去维护他所有因子的最小位置\(nx[j]\),就能得到当前这个位置所能到达的最远位置.那这样我们就可以从\(l\)开始往后面跳一直跳到\(r\)位置,看跳了多少次,但是这样的话会超时,所以我们就可以用倍增法来进行优化.
-
代码:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int n,q; int a[N]; int nx[N]; int dp[N][30]; vector<int> prime[N]; void init(){ for(int i=2;i<=100000;++i){ if(prime[i].empty()){ nx[i]=n+1; for(int j=i;j<=100000;j+=i){ prime[j].pb(i); } } } } int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>n>>q; rep(i,1,n) cin>>a[i]; init(); dp[n+1][0]=n+1; per(i,n,1){ dp[i][0]=dp[i+1][0]; for(auto w:prime[a[i]]){ dp[i][0]=min(dp[i][0],nx[w]); nx[w]=i; } } rep(i,1,20){ rep(j,1,n+1){ dp[j][i]=dp[dp[j][i-1]][i-1]; } } while(q--){ int l,r; cin>>l>>r; int ans=1; per(i,20,0){ if(dp[l][i]<=r){ ans+=(1<<i); l=dp[l][i]; } } cout<<ans<<'\n'; } return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮