【BZOJ4052】【Cerc2013】Magical GCD 单调栈
2016-06-04 09:10
579 查看
题目大意:给出一个正整数序列,求一个连续子序列,使得这个连续子序列的GCD与长度的乘积最大化,并输出最大值。
首先不难想出N*N的做法(假装GCD是常数):穷举右端点再枚举左端点,然后我们发现如果固定右端点,那么左端点从右往左扫的时候,GCD单调不增,因此可以用单调栈来维护,对于每一个GCD,只记录一个最远的左端点,又因为单调栈中的元素每次改变至少要减少一半,所以元素最多不超过log2(10^12)个,每次右端点移动是维护单调栈并更新答案即可。
/**************************************************************
Problem: 4052
User: cqyzhb
Language: C++
Result: Accepted
Time:820 ms
Memory:7836 kb
****************************************************************/
#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define oo 999999999
#define MAXN 210005
void _read(int &x)
{
char ch=getchar(); x=0; bool flag=false;
while(ch<'0' || ch>'9'){if(ch=='-')flag=true; ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}if(flag)x=-x; return ;
}
void _readLL(long long &x)
{
char ch=getchar(); x=0; bool flag=false;
while(ch<'0' || ch>'9'){if(ch=='-')flag=true; ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}if(flag)x=-x; return ;
}
int N,maxN=0;long long A[MAXN];
void Init()
{
_read(N);maxN=max(maxN,N);
for(int i=0;i<=N+1;i++)A[i]=0;
for(int i=1;i<=N;i++)_readLL(A[i]);
return ;
}
long long gcd(long long x,long long y)
{
if(x==0&&y==0)printf("Fuck");
long long t;
while(y){t=x%y; x=y; y=t; }
return x;
}
int sta[MAXN],top,sta_[MAXN],top_;
long long stb[MAXN],stb_[MAXN];
long long ans=0;
void work()
{
for(int i=1;i<=N;i++)sta[i]=stb[i]=sta_[i]=stb_[i]=0;
sta[1]=1; top=1; ans=A[1]; stb[1]=A[1];
for(int i=2;i<=N;i++)
{
ans=max(ans,A[i]);
sta[++top]=i; stb[top]=A[i];
for(int j=top-1;j>=1;j--)
{
stb[j]=gcd(stb[j],stb[j+1]);
}
top_=0;
for(int j=1;j<=top;j++)
{
if(stb[j]>stb[j-1] || j==1)
{
sta_[++top_]=sta[j]; stb_[top_]=stb[j];
}
}
top=top_;
for(int j=1;j<=top;j++)
{
sta[j]=sta_[j]; stb[j]=stb_[j];
ans=max(ans,stb[j] * (long long)(i-sta[j]+1));
}
}
//cout<<ans<<endl;
printf("%lld\n",ans);
return ;
}
int main()
{
// freopen("c1.in","r",stdin);
// freopen("out.txt","w",stdout);
int t;scanf("%d",&t);
while(t--)
{
Init();
work();
}
// printf("%d",maxN);
return 0;
}
首先不难想出N*N的做法(假装GCD是常数):穷举右端点再枚举左端点,然后我们发现如果固定右端点,那么左端点从右往左扫的时候,GCD单调不增,因此可以用单调栈来维护,对于每一个GCD,只记录一个最远的左端点,又因为单调栈中的元素每次改变至少要减少一半,所以元素最多不超过log2(10^12)个,每次右端点移动是维护单调栈并更新答案即可。
/**************************************************************
Problem: 4052
User: cqyzhb
Language: C++
Result: Accepted
Time:820 ms
Memory:7836 kb
****************************************************************/
#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define oo 999999999
#define MAXN 210005
void _read(int &x)
{
char ch=getchar(); x=0; bool flag=false;
while(ch<'0' || ch>'9'){if(ch=='-')flag=true; ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}if(flag)x=-x; return ;
}
void _readLL(long long &x)
{
char ch=getchar(); x=0; bool flag=false;
while(ch<'0' || ch>'9'){if(ch=='-')flag=true; ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}if(flag)x=-x; return ;
}
int N,maxN=0;long long A[MAXN];
void Init()
{
_read(N);maxN=max(maxN,N);
for(int i=0;i<=N+1;i++)A[i]=0;
for(int i=1;i<=N;i++)_readLL(A[i]);
return ;
}
long long gcd(long long x,long long y)
{
if(x==0&&y==0)printf("Fuck");
long long t;
while(y){t=x%y; x=y; y=t; }
return x;
}
int sta[MAXN],top,sta_[MAXN],top_;
long long stb[MAXN],stb_[MAXN];
long long ans=0;
void work()
{
for(int i=1;i<=N;i++)sta[i]=stb[i]=sta_[i]=stb_[i]=0;
sta[1]=1; top=1; ans=A[1]; stb[1]=A[1];
for(int i=2;i<=N;i++)
{
ans=max(ans,A[i]);
sta[++top]=i; stb[top]=A[i];
for(int j=top-1;j>=1;j--)
{
stb[j]=gcd(stb[j],stb[j+1]);
}
top_=0;
for(int j=1;j<=top;j++)
{
if(stb[j]>stb[j-1] || j==1)
{
sta_[++top_]=sta[j]; stb_[top_]=stb[j];
}
}
top=top_;
for(int j=1;j<=top;j++)
{
sta[j]=sta_[j]; stb[j]=stb_[j];
ans=max(ans,stb[j] * (long long)(i-sta[j]+1));
}
}
//cout<<ans<<endl;
printf("%lld\n",ans);
return ;
}
int main()
{
// freopen("c1.in","r",stdin);
// freopen("out.txt","w",stdout);
int t;scanf("%d",&t);
while(t--)
{
Init();
work();
}
// printf("%d",maxN);
return 0;
}
相关文章推荐
- 第二阶段团队站立会议09
- JAVA中字符编码研究
- alloc 函数
- java.lang.UnsatisfiedLinkError: Couldn’t load locSDK3: findLibrary returned null.
- RMQ算法
- js判断当前浏览器是否为IE
- 【CG】粒子系统-变色龙
- 一切成功源于积累——20160604 美国非农15分钟k线直至收盘 各货币对表现 大幅不及预期 黄金3000点
- Python 中的Pyc文件
- 6.4站立会议2
- Java之日期和时间的计算
- 数论之同余
- 101. Symmetric Tree
- hdu 1875 畅通工程再续(最小生成树,prim)
- 详解Java中用于国际化的locale类
- android清理内存缓存和文件缓存
- Vijos P1531 食物链
- 个人最终总结
- 将博客搬至CSDN
- 第十章 存储过程和函数