【JZOJ3895】数字对【ST表】
题目大意:
题目链接:https://jzoj.net/senior/#main/show/3895
给出序列,求最长的子序列,使得中有一个位置能被所有中的数整除。
思路:
以上不重要
明显可以用表来做。维护两个数组和。其中表示以开始,接下来位的最小值,表示以开始,接下来位的最大公约数。
很明显
然后回答就可以了。
时间复杂度。
如果您是可以使用不带修改的线段树和优秀的玄学暴力。
代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N=500010;
const int LG=20;
int n,l,r,mid,Min[N][LG+1],Gcd[N][LG+1];
queue<int> q;
bool check(int len) //二分长度
{
int k=log2(len);
bool ok=0;
for (int i=1;i<=n-len+1;i++)
{
int minn=min(Min[i][k],Min[i+len-(1<<k)][k]);
int GCD=__gcd(Gcd[i][k],Gcd[i+len-(1<<k)][k]);
if (minn==GCD)
{
if (!ok)
{
ok=1;
while (q.size()) q.pop();
}
q.push(i); //答案队列
}
}
return ok;
}
int main()
{
scanf("%d",&n);
memset(Min,0x3f3f3f3f,sizeof(Min));
for (int i=1;i<=n;i++)
{
scanf("%d",&Min[i][0]);
Gcd[i][0]=Min[i][0];
}
for (int j=1;j<=LG;j++)
for (int i=1;i+(1<<(j-1))<=n;i++) //RMQ不解释
{
Min[i][j]=min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]);
Gcd[i][j]=__gcd(Gcd[i][j-1],Gcd[i+(1<<(j-1))][j-1]);
}
l=1;
r=n;
while (l<=r) //二分答案长度
{
mid=(l+r)/2;
if (check(mid)) l=mid+1;
else r=mid-1;
}
printf("%d %d\n",q.size(),l-2);
while (q.size())
{
printf("%d ",q.front());
q.pop();
}
return 0;
}