原题链接
题意:
给一个正数数组,每一步操作会把所有ai变成gcd(ai, ai+1),对于an=gcd(an, a1),求最少多少步之后会使得所有数都相同。
思路:
答案具有单调性,所以可以二分答案,查询操作可以用线段树或稀疏表维护一下区间最大公因数,题目给了四秒,基本怎么写都不会超时,我线段树跑了1169ms,稀疏表跑了549ms,试了一下其他人的稀疏表可以跑到300多ms。
线段树
#include<bits/stdc++.h>
#define LL long long
#define INF INT64_MAX
#define MOD 998244353
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;
typedef pair<int,int>pa;
const int N = 2e5+7;
int a[N<<1], SegTree[N<<3];
void build(int rt, int l, int r)
{
if(l==r)
{
SegTree[rt] = a[l];
return;
}
int mid = l+r>>1;
build(ls, l, mid);
build(rs, mid+1, r);
SegTree[rt] = __gcd(SegTree[ls], SegTree[rs]);
}
int query(int rt, int l, int r, int ql, int qr)
{
if(l>=ql && r<=qr) return SegTree[rt];
int mid = l+r>>1;
int res = 0;
if(mid>=ql) res = __gcd(res, query(ls, l, mid, ql, qr));
if(mid< qr) res = __gcd(res, query(rs, mid+1, r, ql, qr));
return res;
}
int main()
{
int t, n;
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
int f = 0;
for(int i = 1;i <= n;i++)
{
scanf("%d", &a[i]);
f = __gcd(f, a[i]);
}
for(int i = 1;i <= n;i++)
{
a[i]/=f;
a[i+n]=a[i];
}
n<<=1;
build(1, 1, n);
int ans = 0;
for(int i = 1;i <= n/2;i++)
{
if(a[i]==1) continue;
int l = i, r = n;
while(l<r)
{
int mid = l+r>>1;
if(query(1, 1, n, i, mid)==1) r = mid;
else l = mid+1;
}
ans = max(ans, r-i);
}
printf("%d\n", ans);
}
return 0;
}
稀疏表
#include<bits/stdc++.h>
#define LL long long
#define INF INT64_MAX
#define MOD 998244353
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;
typedef pair<int,int>pa;
const int N = 2e5+7;
int a[N<<1], dp[N<<1][30], n;
void init()
{
for(int j = 0;j < 18;j++)
{
for(int i = 1;i+(1<<j)-1 <= n;i++)
{
if(!j) dp[i][j] = a[i];
else dp[i][j] = __gcd(dp[i][j-1], dp[i+(1<<j-1)][j-1]);
}
}
}
int query(int l, int r)
{
int len = r-l+1;
int k = log(len)/log(2);
return __gcd(dp[l][k], dp[r-(1<<k)+1][k]);
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
int f = 0;
for(int i = 1;i <= n;i++)
{
scanf("%d", &a[i]);
f = __gcd(f, a[i]);
}
for(int i = 1;i <= n;i++)
{
a[i]/=f;
a[i+n]=a[i];
}
n<<=1;
init();
int ans = 0;
for(int i = 1;i <= n/2;i++)
{
if(a[i]==1) continue;
int l = i, r = n;
while(l<r)
{
int mid = l+r>>1;
if(query(i, mid)==1) r = mid;
else l = mid+1;
}
ans = max(ans, r-i);
}
printf("%d\n", ans);
}
return 0;
}