hdu 5381

Posted yinwuxiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu 5381相关的知识,希望对你有一定的参考价值。

题解:

还是比较水的一道题

首先可以发现每个数最多被除log次,所以有连续一段相同

然后我想的是变成矩形统计前缀和问题用主席树来维护

然后发现这题很卡空间

qwq acm依旧很多64mb的题

首先比较重要的一点是

这题如果不用标记永久化

需要用到down

主席树down需要新开节点(随意yy一下就知道了)

当然为了节省空间我们选择标记永久化

这时候我们就要记录两个标记

一个代表不用传递到儿子的data

一个代表要传递到儿子的lazy

至于为什么自己想一下就知道了

另外这题的方法应该是将询问离线(离线很重要啊)

然后把每个询问拆成两个,sort一遍再做

这样就是线段树的空间了

正解:

另外这题应该还有一个做法,就是直接对整个序列建线段树是在线的(我觉得这个应该才算正解)

然后维护每个区间中子区间gcd的和,和边界区间对应的log个数值,以及对应个数

然后区间合并显然可以通过这些信息来维护

而查询的时候先找出对应区间,用和updata一样的方法维护答案

不过这样updata的时候应该是logn^2的

所以时间应该是nlogn^3的

线段树很多时候就是像这样利用区间合并 下次应该要注意一下

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define IL inline
#define rint register int 
#define rep(i,h,t) for (rint i=h;i<=t;i++)
#define dep(i,t,h) for (rint i=t;i>=h;i--)
#define mid ((h+t)/2)
IL int max(int x,int y)
{
  if (x>y) return(x);
  else return(y);
}
IL int min(int x,int y)
{
  if (x<y) return(x);
  else return(y);
}
IL void swap(int &x,int &y)
{
  int tmp=x;x=y,y=tmp;
}
struct re{
  int a,b;
};
vector<re> ve,ve1;
const int N=1.1e4;
const int N2=3e6;
int n,m,a[N],root[N],cnt,ls[N2],rs[N2],lazy[N2];
ll data[N2];
int gcd(int x,int y)
{
  if(!y) return(x);
  return(gcd(y,x%y));
}
void change(int last,int &x,int h,int t,int h1,int t1,int k)
{
  x=++cnt;
  ls[x]=ls[last]; rs[x]=rs[last]; lazy[x]=lazy[last]; data[x]=data[last];
  data[x]+=1ll*k*(min(t,t1)-max(h,h1)+1);
  if (h1<=h&&t<=t1)
  {
    lazy[x]+=k; return;
  }
  if (h1<=mid) change(ls[last],ls[x],h,mid,h1,t1,k);
  if (mid<t1) change(rs[last],rs[x],mid+1,t,h1,t1,k);
}
ll query2(int x,int h,int t,int h1,int t1,int k)
{
  if (h1<=h&&t<=t1) return(data[x]+k*(t-h+1));
  ll ans=0;
  if (h1<=mid) ans+=query2(ls[x],h,mid,h1,t1,k+lazy[x]);
  if (mid<t1) ans+=query2(rs[x],mid+1,t,h1,t1,k+lazy[x]);
  return(ans);
}
IL ll query(int x,int h,int t,int h1,int t1)
{
  if (h1>t1) return(0);
  return(query2(x,h,t,h1,t1,0));
}
int main()
{
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  int T;
  scanf("%d",&T);
  while (scanf("%d",&n)!=EOF)
  {
    rep(i,1,n) scanf("%d",&a[i]);
    cnt=0;
    memset(ls,0,sizeof(ls));
    memset(rs,0,sizeof(rs));
    memset(lazy,0,sizeof(lazy));
    ve.clear(); 
    rep(i,1,n)
    {
      ve1.clear();
      int tmp=a[i],last=i;
      root[i]=root[i-1];
      ve1.push_back((re){i,tmp});
      int len=ve.size()-1;
      rep(j,0,len)
      {
        int tmp2=tmp;
        tmp=gcd(tmp,ve[j].b);
        if (tmp2!=tmp)
        { 
          ve1.push_back((re){ve[j].a,tmp});
          change(root[i],root[i],1,n,ve[j].a+1,last,tmp2);
          last=ve[j].a;
        }
      }
      change(root[i],root[i],1,n,1,last,tmp);
      ve.swap(ve1); 
    }
    scanf("%d",&m);
    rep(i,1,m)
    {
      int l,r;
      scanf("%d%d",&l,&r);
      printf("%lld\n",query(root[r],1,n,1,r)-query(root[r],1,n,1,l-1)); 
    }
  }
  return 0; 
}

 

以上是关于hdu 5381的主要内容,如果未能解决你的问题,请参考以下文章

hdu 5381 The sum of gcd 莫队+预处理

hdu 5381 The sum of gcd 2015多校联合训练赛#8莫队算法

Hdu5381 the sum of gcd(莫队)

hdu5381维护区间内所有子区间的gcd之和-线段树

HDU - 5381 The sum of gcd(莫队/线段树区间合并)

HDU - 5381 The sum of gcd(莫队/线段树区间合并)