51Nod1203 2012集训队答辩 JZPLCM

A1339. JZPLCM(顾昱洲)

时间限制:3.0s   内存限制:256.0MB

试题来源

  2012中国国家集训队命题答辩

问题描述

  给定一长度为n的正整数序列a,有q次询问,每次询问一段区间内所有数的lcm(即最小公倍数)。由于答案可能很大,输出答案模1000000007。

输入格式

  第一行,两个整数,n, q,分别表示数列长度和询问个数。
  下面n行,每行一个整数,第i行的整数为ai
  下面q行,每行两个整数l, r,表示询问下标i在[l, r]范围内的ai的lcm。

输出格式

  q行。对于每个询问,输出一行,表示对应的答案。

样例输入

3 3
123
234
345
1 2
2 3
1 3

样例输出

9594
26910
1103310  

数据范围

n,q<10w,ai<=1e9。

这题这么神我肯定要放上来啊。

首先51Nod的数据是经过弱化的,并没有那么强。

对于51Nod的数据,我们可以用一种比较皮的方法艹过去。

一些数的LCM就是对每个质数的指数取max。
对于大于sqrt(x)的质数,这个指数最大是1。所以对于这些数,我们只需要判断在这些区间内是否出现过就可以了。这个问题可以用莫队算法+桶实现。
对于小于等于sqrt(x)的质数,经过打表发现这种质数只会有约50个。我们可以把每一个数S[i]分解质因数后存进50个表里,每次询问对这50个质数分别来一次区间求最值。因为表定型后不涉及修改操作,可以使用ST表把单次询问降低到O(1)。
所以说这道题需要写两个程序。
莫队算法转移的复杂度是O(1)的,该部分复杂度是O(n*sqrt(n))。
倍增算法预处理的复杂度是O(50*n*log(n))的,处理询问复杂度是O(50*n)的。
鉴于这题空间不是很足,要对每个小质数一个一个处理ST表,空间复杂度为O(n*log(n)+n*50)。

但是对于Ai<=1e9就无能为力了。

然后队爷给了一种很强力的做法,发现我的思维能力跟队爷完全不在一个时代上。

一般我做题,先把问题模型化,一般化,再根据数据范围选择合适的算法/数据结构一顿爆艹。

但是队爷做题有两种思路,这就比我不知高到哪里去了。

一种思路:原问题 - 一般化(强化)的问题 - 解决一般化的问题 - 解决原问题。

另一种思路是:先思考某种特殊情况,一般是比较好处理的,再推广到原问题。

其实这种思路在很多解题报告中也有出现:

“我们讨论只有一条链的情况。”

“我们先来看一下没有约束的情况。”

“这个a很讨厌,我们先不管它。”

……

划下重点。

所以我们先来假设每一个数都是无平方因子数。

这种情况我只会莫队,果然是数据结构学傻(不对数据结构也不会写)。

这时候问题化为:某区间所有出现过的数的积(不同数的积)。

这是一个我不会的经典问题,好像之前看到有些题考这个?

对于上面的问题,我们考虑对于一组询问[l,r]。

找到每个数上一次出现的位置pre[i](如果没有出现就是0)。

那么就要求的是

对于这种问题,可以有如下解决方式:

设询问为f(l,r,l),则询问等价为

所以你要写一种数据结构,支持上面的操作。

显然可以离线+树状数组。

那么有平方甚至多次方因子呢?

假设一个数A[i]可以写成p的q次方(p为质数,q为满足被整除的最大值)。

那可以把这个数拆成q个数p,p^2,p^3……p^q,每个数的权值都是p。

于是问题就转化成了上面的弱化问题,只是区间长度变了而已。

于是这题就解决了(好神啊突然开车)。

略显尴尬的是我的代码比较长……

下面放对于51Nod数据的爆艹法。

#include    <iostream>
#include    <cstdio>
#include    <cstdlib>
#include    <algorithm>
#include    <vector>
#include    <cstring>
#include    <queue>
#include    <complex>
#include    <stack>
#define LL long long int
#define dob double
#define FILE "JZPLCM"
using namespace std;

const int N = 50010;
const int Mod = 1e9+7;
struct Node{int to,next;}E[N];
int n,m,P[N],vis[N],tot,S[N],Ny[N],block,A[20][N],bel[N],bin[N],B[51][N];
int Log[N],Pw[51][N],Ans[N],res=1,head[N],Tot;
struct Data{
  int l,r,id;
  bool operator <(const Data &d)const{
    if(bel[l]==bel[d.l])return r<d.r;
    return bel[l]<bel[d.l];
  }
}Ask[N];

inline int gi(){
  int x=0,res=1;char ch=getchar();
  while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)res*=-1;ch=getchar();}
  while(ch<=‘9‘&&ch>=‘0‘)x=x*10+ch-48,ch=getchar();
  return x*res;
}

inline void link(int u,int v){
  E[++tot]=(Node){v,head[u]};
  head[u]=tot;
}

inline void prepare(){
  for(int i=2;i<N;++i){
    if(!vis[i])P[++tot]=i;
    for(int j=1;j<=tot;++j){
      int ytk=i*P[j];if(ytk>=N)break;
      vis[ytk]=1;if(i%P[j]==0)break;
    }
  }
  Ny[1]=1;
  for(int i=2;i<N;++i)
    Ny[i]=1ll*(Mod-Mod/i)*Ny[Mod%i]%Mod;
}

inline void Insert(int x){
  for(int e=head[x];e;e=E[e].next){
    int y=E[e].to;bin[y]++;
    if(bin[y]==1)res=1ll*res*y%Mod;
  }
}

inline void Delete(int x){
  for(int e=head[x];e;e=E[e].next){
    int y=E[e].to;bin[y]--;
    if(bin[y]==0)res=1ll*res*Ny[y]%Mod;
  }
}

inline void Modui(){
  memset(bin,0,sizeof(bin));
  Insert(S[1]);int l=1,r=1;
  for(int i=1;i<=m;++i){
    if(r<Ask[i].r)
      for(int j=r+1;j<=Ask[i].r;++j)
        Insert(S[j]);
    if(r>Ask[i].r)
      for(int j=r;j>Ask[i].r;--j)
        Delete(S[j]);
    if(l<Ask[i].l)
      for(int j=l;j<Ask[i].l;++j)
        Delete(S[j]);
    if(l>Ask[i].l)
      for(int j=l-1;j>=Ask[i].l;--j)
        Insert(S[j]);
    l=Ask[i].l;r=Ask[i].r;
    Ans[Ask[i].id]=1ll*Ans[Ask[i].id]*res%Mod;
  }
}

int main()
{
  freopen(FILE".in","r",stdin);
  freopen(FILE".out","w",stdout);

  prepare();n=gi();m=gi();block=sqrt(n);
  bin[0]=1;for(int i=1;i<=16;++i)bin[i]=bin[i-1]*2;
  for(int i=1,j=0;i<=n;i*=2,j++)Log[i]=j;
  for(int i=1;i<=N;++i)
    if(!Log[i])Log[i]=Log[i-1];
  for(int i=1;i<=n;++i)S[i]=gi(),bel[i]=i/block+1;
  for(int j=1;j<=n;++j){
    int ytk=S[j];
    for(int i=1;i<=50;++i){
      int num=0;
      while(ytk%P[i]==0)num++,ytk/=P[i];
      B[i][j]=num;
      if(ytk==1)break;
    }
    if(ytk>1)link(S[j],ytk);
  }
  for(int i=1;i<=50;++i){
    Pw[i][0]=1;
    for(int j=1;j<=19;++j)
      Pw[i][j]=1ll*Pw[i][j-1]*P[i]%Mod;
  }
  for(int i=1;i<=m;++i){
    int l=gi(),r=gi();Ans[i]=1;
    Ask[i]=(Data){l,r,i};
  }
  for(int k=1;k<=50;++k){
    for(int i=1;i<=n;++i)A[0][i]=B[k][i];
    for(int i=1;i<=16;++i)
      for(int j=1;j<=n;++j)
        A[i][j]=max(A[i-1][j],A[i-1][min(n,j+bin[i-1])]);
    for(int i=1;i<=m;++i){
      int l=Ask[i].l,r=Ask[i].r,dt=r-l+1,Lg=Log[dt];
      int Mx=max(A[Lg][l],A[Lg][r-bin[Lg]+1]);
      Ans[i]=1ll*Ans[i]*Pw[k][Mx]%Mod;
    }
  }
  sort(Ask+1,Ask+m+1);Modui();
  for(int i=1;i<=m;++i)
    printf("%d\n",Ans[i]);
  return 0;
}

莫队+倍增

然后是对于51Nod的数据很多地方跑不过原数据的树状数组。

#include    <iostream>
#include    <cstdio>
#include    <cstdlib>
#include    <algorithm>
#include    <vector>
#include    <cstring>
#include    <queue>
#include    <complex>
#include    <stack>
#define LL long long int
#define dob double
#define FILE "JZPLCM"
#define c233 printf("\n");
using namespace std;

const int N = 50010;
const int M = N*50;
const int Mod = 1e9+7;

struct Data{
  int r,tr,kind,id;
  bool operator <(const Data &d)const{
    return tr<d.tr;
  }
}opt[N*40];

struct Pair{
  int pre,id,val;
  Pair(){};
  Pair(int p,int i,int v){pre=p;id=i;val=v;}
  bool operator <(const Pair &p)const{
    return pre<p.pre;
  }
}Y[M];

int n,m,P[N],tot,vis[N],A[N],cnt,ED[N],pre[M],bin[N],top,Ans[N],W[M],V[M];
struct BIT{
  int T[M];
  inline void clear(){
    for(int i=0;i<=cnt;++i)T[i]=1;
  }
  inline int lb(int k){
    return k&-k;
  }
  inline void update(int x,int val){
    for(;x<=cnt;x+=lb(x))T[x]=1ll*T[x]*val%Mod;
  }
  inline int query(int x,int res=1){
    for(;x>0;x-=lb(x))res=1ll*res*T[x]%Mod;
    return res;
  }
}T;

inline int gi(){
  int x=0,res=1;char ch=getchar();
  while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)res*=-1;ch=getchar();}
  while(ch<=‘9‘&&ch>=‘0‘)x=x*10+ch-48,ch=getchar();
  return x*res;
}

inline void prepare(){
  for(int i=2;i<N;++i){
    if(!vis[i])P[++tot]=i;
    for(int j=1,ytk;j<=tot;++j){
      ytk=i*P[j];if(ytk>=N)break;
      vis[ytk]=1;if(i%P[j]==0)break;
    }
  }
}

inline int QPow(int d,int z,int res=1){
  for(;z;z>>=1,d=1ll*d*d%Mod)
    if(z&1)res=1ll*res*d%Mod;
  return res;
}

inline void solve(){
  int i=1,j=1;T.clear();
  while(i<=cnt && j<=top){
    if(Y[i].pre<=opt[j].tr)
      T.update(Y[i].id,Y[i].val),++i;
    else{
      int id=opt[j].id;
      if(opt[j].kind==1)Ans[id]=1ll*Ans[id]*T.query(opt[j].r)%Mod;
      else Ans[id]=1ll*Ans[id]*QPow(T.query(opt[j].r),Mod-2)%Mod;
      ++j;
    }
  }
  while(j<=top){
    int id=opt[j].id;
    if(opt[j].kind==1)Ans[id]=1ll*Ans[id]*T.query(opt[j].r)%Mod;
    else Ans[id]=1ll*Ans[id]*QPow(T.query(opt[j].r),Mod-2)%Mod;
    ++j;
  }
}

int main()
{
  freopen(FILE".in","r",stdin);
  freopen(FILE".out","w",stdout);
  n=gi();m=gi();prepare();
  for(int i=1;i<=n;++i)A[i]=gi();
  for(int i=1;i<=n;++i){
    int ytk=A[i];
    for(int j=1;P[j]<=ytk/P[j];++j)
      if(ytk%P[j]==0){
        W[++cnt]=P[j];ytk/=P[j];V[cnt]=P[j];
        while(ytk%P[j]==0)
          ++cnt,W[cnt]=W[cnt-1]*P[j],V[cnt]=P[j],ytk/=P[j];
      }
    if(ytk>1)W[++cnt]=ytk,V[cnt]=ytk;
    ED[i]=cnt;
  }
  for(int i=1;i<=cnt;++i)pre[i]=bin[W[i]],bin[W[i]]=i;
  for(int i=1;i<=cnt;++i)Y[i]=Pair(pre[i],i,V[i]);
  sort(Y+1,Y+cnt+1);
  for(int i=1;i<=m;++i){
    int l=gi(),r=gi();Ans[i]=1;
    opt[++top]=(Data){ED[r],ED[l-1],1,i};
    opt[++top]=(Data){ED[l-1],ED[l-1],-1,i};
  }
  sort(opt+1,opt+top+1);solve();
  for(int i=1;i<=m;++i)printf("%d\n",Ans[i]);
  return 0;
}

树状数组

时间: 2024-10-20 04:34:56

51Nod1203 2012集训队答辩 JZPLCM的相关文章

CTSC2016游记

5.1 坐了很久的火车终于到了北京,然后又坐地铁,非常疲惫,然后听说明天就是考试-- 然而晚上还是出去浪,并决定要买三国杀,但是用谋财地图找了好几个书报亭都没找到-- 5.2 YJQYJQ?YJQYJQ! 和WC一样是纸质试题.比赛开始,我先花了一个多小时看懂了第一题的题面,然后敲完了前两题的裸暴力,然后大概剩下三个半小时玩第三题提答. 然后因为我是傻逼,又出了一点偏差,本来应该30多分的提答结果只有24分,于是总分只有5+5+24=34分了--感觉真的是爆炸了啊.AwD爷第三题56分,非常劲啊

[国家集训队2012]tree(陈立杰)

[国家集训队2012]tree(陈立杰) 题目 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树.题目保证有解. INPUT 第一行V,E,need分别表示点数,边数和需要的白色边数.接下来E行每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色). OUTPUT 一行表示所求生成树的边权和. SAMPLE INPUT 2 2 1 0 1 1 1 0 1 2 0 OUTPUT 2 数据规模 0:V<=101,2,3:V<=

[国家集训队2012]JZPFAR

[国家集训队2012]JZPFAR 题目 平面上有n个点.现在有m次询问,每次给定一个点(px, py)和一个整数k,输出n个点中离(px, py)的距离第k大的点的标号.如果有两个(或多个)点距离(px, py)相同,那么认为标号较小的点距离较大. INPUT 第一行,一个整数n,表示点的个数.下面n行,每行两个整数x_i, y_i,表示n个点的坐标.点的标号按照输入顺序,分别为1..n.下面一行,一个整数m,表示询问个数.下面m行,每行三个整数px_i, py_i, k_i,表示一个询问.

BZOJ 2622 [2012国家集训队测试]深入虎穴 SPFA

题意:链接 方法: SPFA 解析: 这题写深搜是作死,别问我为什么. 深搜过程您大概会RE8个点- -! 复杂度类似O(nm)? 其实总体东西就是个SPFA,不过这里面我们需要做一些改动. 这只老虎是DP老虎,他每次会占据你的最优方案. 所以使得你只能用次优方案来更新最优方案以及次优方案. 这就是SPFA的路子. 但是这个路子并不够. 让我们考虑一个点只有一个儿子的时候. 他可能会扫一圈后第一次让他的儿子更新一次答案. 这时候是有最短路而并没次短路的. 但是您肯定会更新一下它的儿子信息. 这时

[COGS 1799][国家集训队2012]tree(伍一鸣)

Description 一棵n个点的树,每个点的初始权值为1.对于这棵树有q个操作,每个操作为以下四种操作之一: + u v c:将u到v的路径上的点的权值都加上自然数c: - u1 v1 u2 v2:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树: * u v c:将u到v的路径上的点的权值都乘上自然数c: / u v:询问u到v的路径上的点的权值和,求出答案对于51061的余数. Input 第一行两个整数n,q 接下来n-1行每行两个正整数u,v,

【国家集训队2012】tree(伍一鸣)

题面 传送门 Sol 这不是一道LCT模板题吗? 和线段树一样维护区间加法和乘法标记 记得要更新自己本身的权值 这种题就该一遍AC # include <bits/stdc++.h> # define RG register # define IL inline # define Fill(a, b) memset(a, b, sizeof(a)) # define Sqr(x) ((x) * (x)) # define ls ch[0][x] # define rs ch[1][x] usi

jzoj 2867. 【集训队互测 2012】Contra

Description 偶然间,chnlich 发现了他小时候玩过的一个游戏"魂斗罗",于是决定怀旧.但是这是一个奇怪的魂斗罗 MOD. 有 N 个关卡,初始有 Q 条命. 每通过一个关卡,会得到 u 分和1条命,生命上限为 Q.其中 u=min(最近一次连续通过的关数,R). 若没有通过这个关卡,将会失去1条命,并进入下一个关卡. 当没有生命或没有未挑战过的关卡时,游戏结束,得到的分数为每关得到的分数的总和. 由于 chnlich 好久不玩这个游戏了,每条命通过每个关卡的概率均为p(

[JZOJ2866] 【集训队互测 2012】Bomb

题目 题目大意 给你一个有\(n\)个点的平面. 选择三个点,求两两之间曼哈顿距离和的最大值和最小值. 思考历程&正解 比赛的时候没有想太多,但感觉似乎比较水-- 首先有个很显然的性质,答案为这三个点的最大最小横坐标之差和最大最小纵坐标之差的和. 可以把它看成矩形的周长,容易发现矩形至少一个顶点是三个点之一. 后来才发现水的是求最大值,而不是求最小值. 比赛之后开始和WMY刚-- 最大值是很好求的.我一开始打了个线段树来求,后来发现根本不用-- 求出所有点的\(Xmin,Xmax,Ymin,Ym

jzoj 2866. 【集训队互测 2012】Bomb

Description 给你\(n\)个点,坐标分别为\((xi,yi)\).从中取出三个点,使得其两两间曼哈顿距离和最大和最小,求最大值和最小值. 对于 100% 的数据, N<=100000 , 0<=Xi,Yi<=10^8 Solution 看完题目后感觉要分类讨论,思考1h后果断暴力O(n^3). 但是判断了一下n<=500才跑暴力,得了30分.(?10^9过4s?不判50分) 其实可以O(n^2)暴力的.(听说O(n^2)+优化 = 100) 对于第一个最大值,我们可以\