您的位置:首页 > Web前端

HDU 5869 Different GCD Subarray Query(2016大连网络赛 B 树状数组+技巧)

2016-09-13 21:34 477 查看

还是想不到,真的觉得难,思路太巧妙

题意:给你一串数和一些区间,对于每个区间求出区间内每段连续值的不同gcd个数(该区间任一点可做起点,此点及之后的点都可做终点)

 

首先我们可以知道每次添加一个值时gcd要么不变要么减小,并且减小的幅度很大,就是说固定右端点时最多只能有(log2 a)个不同的gcd,而且我们知道gcd(gcd(a,b),c)=gcd(a,gcd(b,c)),所以我们可以使用n*(log2 n)的时间预处理出每个固定右端点的不同gcd的值和位置。解法就是从左到右,每次只需要使用上一次的不同gcd的值和位置。

离线处理每个询问,按照区间右端点从小到大排序,把预处理的每个gcd依次加入树状数组中,这样到一个询问的右端点时就区间查询不同gcd的个数(只需要使用左端点的位置)。树状数组在每种gcd最右一个左端点的位置(贪心想法)存这儿gcd的个数,但可能这个gcd之前出现过,因此我们可以再开一个数组存每种gcd的位置(保证每种gcd在最右边的位置)

 

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define eps 1E-8
/*注意可能会有输出-0.000*/
#define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
#define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
#define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
#define mul(a,b) (a<<b)
#define dir(a,b) (a>>b)
typedef long long ll;
typedef unsigned long long ull;
const int Inf=1<<28;
const double Pi=acos(-1.0);
const int Mod=1e9+7;
const int Max=100010;
struct node
{
int mpos,gcd;
} dp[Max][50]; //预处理
struct nide
{
int lef,rig,mpos;
} qes[Max];
int bit[Max],mpos[Max*10];//树状数组处理此位置元素个数 某个gcd的位置(这时的最靠右)
int num[Max],ans[Max],n;
void Swap(int &a,int &b)
{
a^=b;
b^=a;
a^=b;
return;
}
int Gcd(int a,int b)// a、b 均为偶数, gcd(a,b) = 2 * gcd(a/2, b/2)
{
if(a<b)
Swap(a,b);
int c=1; //a为偶数,b为奇数, gcd(a,b) = gcd(a/2 , b)
while(a-b) //a为奇数,b为偶数,gcd(a,b) = gcd(a, b/2)
{
if(a&1) //a、b均为奇数, gcd(a,b) = gcd((a-b)/2, b)
{
if(b&1)
{
if(a>b) a=(a-b)>>1;
else  b=(b-a)>>1;
}
else b>>=1;
}
else
{
if(b&1) a>>=1;
else  c<<=1,a>>=1,b>>=1;
}
}
return c*a;
}
int cntt[Max];
void Init()//预处理固定右端点时左边的g不通gcd
{
int tmp;
cntt[0]=0;
for(int i=1; i<=n; ++i) //固定右端点i
{
cntt[i]=1;
dp[i][1].gcd=num[i];
dp[i][1].mpos=i;
for(int j=1; j<=cntt[i-1]; ++j)
{
tmp=Gcd(dp[i][cntt[i]].gcd,dp[i-1][j].gcd);
if(tmp!=dp[i][cntt[i]].gcd)//与每个右端点形成不同的gcd的位置要尽量靠右
{
dp[i][++cntt[i]].gcd=tmp;
dp[i][cntt[i]].mpos=dp[i-1][j].mpos;
}
}
}
return;
}
bool cmp(struct nide p1,struct nide p2)//排序右端点才可以统计
{
return p1.rig<p2.rig;
}
int lowbit(int x)
{
return x&(-x);
}
void Add(int x,int y)
{
while(x<=n)
{
bit[x]+=y;
x+=lowbit(x);
}
return;
}
int Sum(int x)
{
int sum=0;
while(x)
{
sum+=bit[x];
x-=lowbit(x);
}
return sum;
}
void Solve(int q)
{
memset(bit,0,sizeof(bit));
memset(mpos,0,sizeof(mpos));
int k=1,sum=0;
for(int i=1; i<=n; ++i) //枚举右端点
{
for(int j=1; j<=cntt[i]; ++j)
{
if(!mpos[dp[i][j].gcd])
{
sum++;
Add(dp[i][j].mpos,1);
mpos[dp[i][j].gcd]=dp[i][j].mpos;
}
else if(mpos[dp[i][j].gcd]<dp[i][j].mpos)//保证最右位置
{
Add(mpos[dp[i][j].gcd],-1);
Add(dp[i][j].mpos,1);
mpos[dp[i][j].gcd]=dp[i][j].mpos;
}
}
while(k<=q&&qes[k].rig==i)
{
ans[qes[k].mpos]=sum-Sum(qes[k].lef-1);//计算[lef,rig]的个数
++k;
}
}
return;
}
int main()
{
int q;
while(~scanf("%d %d",&n,&q))
{
for(int i=1; i<=n; ++i)
scanf("%d",&num[i]);
Init();
for(int i=1; i<=q; ++i)
{
scanf("%d %d",&qes[i].lef,&qes[i].rig);
qes[i].mpos=i;
}
sort(qes+1,qes+1+q,cmp);
Solve(q);
for(int i=1; i<=q; ++i)
printf("%d\n",ans[i]);
}
return 0;
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: