FZU2224 An exciting GCD problem 区间gcd预处理+树状数组

分析:(别人写的)

对于所有(l, r)区间,固定右区间,所有(li, r)一共最多只会有log个不同的gcd值,

可以nlogn预处理出所有不同的gcd区间,这样区间是nlogn个,然后对于询问离线处理,

用类似询问区间不同数字的方法,记录每个不同gcd最后出现的位置,然后用树状数组进行维护

注:我是看了这段代码会的,但是他的nlogn预处理我不会,我会nlog^2n的

dp[i][j]代表以i为右端点,向左延伸2^j个点(包括i)的gcd,然后因为这样的gcd满足递减,所以可以二分找区间

代码:

/*RunID: 678021
UserID: 96655
Submit time: 2016-04-19 23:44:20
Language: C++
Length: 2378 Bytes.
Result: Accepted
*/

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=1e4+5;
int T,n,m,dp[N][20];
struct ask{
  int l,r,id;
  bool operator<(const ask &rhs)const{
    return r<rhs.r;
  }
}p[N*10];
struct Seg{
  int l,r,v;
   bool operator<(const Seg &rhs)const{
    return r<rhs.r;
  }
}seg[35*N];
int erfen(int pos,int v){
  int l=1,r=pos;
  while(l<r){
    int mid=(l+r)>>1;
    int len=pos-mid+1;
    int now=pos,cur=-1;
    for(int i=13;i>=0;--i){
      if(len&(1<<i)){
        if(cur==-1)cur=dp[now][i];
        else cur=__gcd(cur,dp[now][i]);
        now-=(1<<i);
      }
    }
    if(cur<v)l=mid+1;
    else r=mid;
  }
  return (l+r)>>1;
}
int hash[35*N],tot,mat[35*N];
int res[N*10];
int c[N];
void add(int x,int t){
  for(int i=x;i<=n;i+=i&(-i))
    c[i]+=t;
}
int get(int x){
  int ans=0;
  if(x==0)return 0;
  for(int i=x;i>0;i-=i&(-i))
    ans+=c[i];
  return ans;
}
int main(){
  scanf("%d",&T);
  while(T--){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
      scanf("%d",&dp[i][0]);
    for(int i=1;i<=m;++i)
      scanf("%d%d",&p[i].l,&p[i].r),p[i].id=i;
    for(int k=1;(1<<k)<=n;++k)
    for(int i=n;i>1;--i){
      int j=i-(1<<k)+1;
      if(j<1)break;
      j=i-(1<<(k-1));
      dp[i][k]=__gcd(dp[i][k-1],dp[j][k-1]);
    }
    int cnt=0;tot=0;
    for(int i=1;i<=n;++i){
      int last=-1;
      for(int j=i;j>0;--j){
        int tmp=dp[j][0];
        if(last!=-1)tmp=__gcd(tmp,last);
        last=tmp;
        ++cnt;
        seg[cnt].l=j,seg[cnt].r=i,seg[cnt].v=last;
        hash[++tot]=last;
        j=erfen(i,last);
      }
    }
    sort(hash+1,hash+1+tot);
    tot=unique(hash+1,hash+1+tot)-hash-1;
    sort(p+1,p+1+m);
    sort(seg+1,seg+1+cnt);
    memset(mat,0,sizeof(mat));
    memset(c,0,sizeof(c));
    int now=1;
    for(int i=1;i<=m;++i){
      for(;now<=cnt&&seg[now].r<=p[i].r;++now){
        int pos=lower_bound(hash+1,hash+1+tot,seg[now].v)-hash;
        if(seg[now].l>mat[pos]){
          if(mat[pos])add(mat[pos],-1);
          add(seg[now].l,1);
          mat[pos]=seg[now].l;
        }
      }
      res[p[i].id]=get(p[i].r)-get(p[i].l-1);
    }
    for(int i=1;i<=m;++i)
      printf("%d\n",res[i]);
  }
  return 0;
}

时间: 2024-10-25 22:14:44

FZU2224 An exciting GCD problem 区间gcd预处理+树状数组的相关文章

POJ 3468 A Simple Problem with Integers 【树状数组】

题目链接:http://poj.org/problem?id=3468 题目大意:给出一组数组v[i],有两种操作,一种给出两个数a,b,要求输出v[a]到v[b]之间的和,另一种给出三个数a,b,c,让v[a]到v[b]之间的数全都加上c. 完全是树状数组能够实现的功能,但是如果就这样单纯的套用模板,做第二种操作是更新每个值,这样的操作就有可能超时. 换一种思路,既然第二种操作是给某区间上的所有数加上相同的值,那么应该是能够简化的才对. 假设数组sum[i]为原数组从v[1]到v[i]的和,数

ACM学习历程—51NOD 1685 第K大区间2(二分 &amp;&amp; 树状数组 &amp;&amp; 中位数)

http://www.51nod.com/contest/problem.html#!problemId=1685 这是这次BSG白山极客挑战赛的E题. 这题可以二分答案t. 关键在于,对于一个t,如何判断它是否能成为第k大. 将序列中大于t的置为1,小于t的置为-1,等于t的置为0.那么区间中位数大于t的和就大于0,小于t的就小于0.于是就是判断区间和大于0的个数是否小于等于k. 维护前缀和sum(i),然后统计之前sum(j)小于sum(i)的有多少个,就是以i为右值的区间和大于0的个数.于

支持区间修改的树状数组

支持区间修改的树状数组 原理 对于一个数组\(a\),以及\(a\)的差分\(c\),显然有\(c[i]=a[i]-a[i-1]\) 那么对于数组a的前缀和有 \(\sum_{i=1}^n{a_i}=c[1]+(c[1]+c[2])+...(c[1]+c[2]+...+c[n])\) 进一步的: \(\sum_{i=1}^n{a_i}=c[1]*n+c[2]*(n-1)+...+c[n]*(n-n+1)\) 展开括号内 \(\sum_{i=1}^n{a_i}=c[1]*n+c[2]*n+...+

HDU 5869 Different GCD Subarray Query (GCD种类预处理+树状数组维护)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5869 问你l~r之间的连续序列的gcd种类. 首先固定右端点,预处理gcd不同尽量靠右的位置(此时gcd种类不超过loga[i]种). 预处理gcd如下代码,感觉真的有点巧妙... 1 for(int i = 1; i <= n; ++i) { 2 int x = a[i], y = i; 3 for(int j = 0; j < ans[i - 1].size(); ++j) { 4 int g

POJ 3468 A Simple Problem with Integers(树状数组区间更新)

A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 97217   Accepted: 30358 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of

A Simple Problem with Integers 多树状数组分割,区间修改,单点求职。 hdu 4267

A Simple Problem with Integers Time Limit: 5000/1500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4032    Accepted Submission(s): 1255 Problem Description Let A1, A2, ... , AN be N elements. You need to deal with

区间素数个数 树状数组 HIT 1867 经理的烦恼

http://acm.hit.edu.cn/hoj/problem/view?id=1867 经理的烦恼   Source : HCPC 2005 Spring   Time limit : 2 sec   Memory limit : 32 M Submitted : 2994, Accepted : 686 Jerry是一家公司销售部门的经理.这家公司有很多连锁店,编号为1,2,3,... Jerry每天必须关注每家连锁店的商品数量及其变化,一项很乏味的工作.在连锁店比较少的时候,Jerry

HDU 4267 A Simple Problem with Integers(树状数组)

A Simple Problem with Integers Time Limit: 5000/1500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4191    Accepted Submission(s): 1309 Problem Description Let A1, A2, ... , AN be N elements. You need to deal with

poj3468 A Simple Problem with Integers (树状数组做法)

题目传送门 A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 142198   Accepted: 44136 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One