Codeforecs 359D Pair of Numbers 单调栈 OR ST表+二分

点击打开链接

题意:n个数a[i],找到最长的一段区间[l,r],存在l<=j<=r 满足a[l],a[l+1]...a[r]都能被a[j]整除
如果最大长度有多个,输出所有开头. n<=3e5,a[i]<=1e6


法1:
枚举a[j] 处理出a[j]的左右边界即可.
因为a|b b|c 所以a|c 用单调栈处理边界 O(n)

法2:
[l,r]中的a[j]为 min(a[l],a[l+1]..a[r]) 要和gcd(a[l]..a[r]) 相等 
预处理出min和gcd的 ST表,二分长度,O(n)判断即可 O(nlogn).

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double eps=1e-10;
const int inf=0x3f3f3f3f;
const int N=5e5+20;
stack<int> s;
int n,a[N],l[N],r[N],f[N];
map<int,int> mp;
vector<int> ans;
int main()
{
	while(cin>>n)
	{
		mp.clear();
		ans.clear();
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			while(!s.empty()&&a[i]%a[s.top()])
			{
				r[s.top()]=i-1;	
				s.pop();
			}
			s.push(i);
		}
		while(!s.empty())
			r[s.top()]=n,s.pop();
		
		for(int i=n;i>=1;i--)
		{
			while(!s.empty()&&a[i]%a[s.top()])
			{
				l[s.top()]=i+1;
				s.pop();	
			}	
			s.push(i);
		}
		while(!s.empty())
			l[s.top()]=1,s.pop();
		
		int mx=0;
		for(int i=1;i<=n;i++)
		{
			f[i]=r[i]-l[i];
			mx=max(mx,f[i]);
			//cout<<l[i]<<' '<<r[i]<<endl;
		}
	
		for(int i=1;i<=n;i++)
			if(f[i]==mx&&!mp[l[i]])
				ans.push_back(l[i]),mp[l[i]]=1;
		cout<<ans.size()<<' '<<mx<<endl;
		for(int i=0;i<ans.size();i++)
			printf("%d ",ans[i]);	
		printf("\n"); 
	}
	return 0;
}

法2代码转自 点击打开链接

#include "cstdio"
#include "iostream"
#include "vector"
#include "algorithm"
#include "math.h"
#include "cstring"
using namespace std;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define maxn 3*100005
#define maxp 20
template <class T>
inline bool read(T &ret)
{
    char c;
    int sgn;
    if(c=getchar(),c==EOF) return 0; //EOF
    while(c!='-'&&(c<'0'||c>'9')) c=getchar();
    sgn=(c=='-')?-1:1;
    ret=(c=='-')?0:(c-'0');
    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    ret*=sgn;
    return 1;
}
int gcd(int a,int b) {if(b!=0) return gcd(b,a%b);else return a;}
int RMQ[maxn][maxp],GCD[maxn][maxp],val[maxn],n,cnt,range;
vector<int> ans;
void ST()
{
    for(int i=1;i<=n;i++) RMQ[i][0]=GCD[i][0]=val[i];
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
    {
        RMQ[i][j]=min(RMQ[i][j-1],RMQ[i+(1<<(j-1))][j-1]);
        GCD[i][j]=gcd(GCD[i][j-1],GCD[i+(1<<(j-1))][j-1]);
    }
}
bool Query(int L,int R)
{
    int k=0;
    while((1<<(k+1))<=R-L+1) k++;
    int a=min(RMQ[L][k],RMQ[R-(1<<k)+1][k]);
    int b=gcd(GCD[L][k],GCD[R-(1<<k)+1][k]);
    if(a==b) return true;
    else return false;
}
bool judge(int v) //枚举r-l
{
    int cc=0;
    vector<int> tt;
    for(int i=1; v+i<=n; i++)
    {
        if(Query(i,i+v)) //L=i,R=i+v;
        {
            cc++;
            tt.push_back(i);
        }
    }
    if(cc>0)
    {
        ans=tt;
        cnt=cc;
        range=v;
        return true;
    }
    return false;
}
int main()
{
    memset(RMQ,1,sizeof(RMQ));
    memset(GCD,1,sizeof(GCD));
    read(n);
    for(int i=1; i<=n; i++)
        read(val[i]);
    ST();
    int l=0,r=n-1,mid;
    while(l<=r)  //二分
    {
        mid=(l+r)>>1;
        if(judge(mid)) l=mid+1;
        else r=mid-1;
    }
    printf("%d %d\n",cnt,range);
    for(int i=0;i<ans.size();i++) {if(i>0) printf(" ");printf("%d",ans[i]);};
    printf("\n");
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值