题目

题目大意

首先有一个定义:

对于一个数,如果和它互质的数可以组成一个等差数列,那么这个数叫“好数”。

现在给你一个数列,有三种操作:

1、询问一段区间内的好数的个数。

2、将一段区间内的数分别模一个值。

3、将某个数修改。


思考历程

先看看这个题目。

好熟悉的题目啊!这不就是初中OJ上的某道数位DP的题吗?

然后发现不是那一道题,松了一口气。

一眼看下去,一定有什么数论。说不定在得到了什么结论之后,就变成一个非常简单的数据结构题了。

然后就在疯狂地推式子……最终没有推出来……

于是就弃疗了。


正解

经过打表找规律后,我们发现:

好数分为三种情况:

1、质数。

2、222的幂次。

3、666

随便想一想,我们很容易知道这些数都是好数。

可问题是,其它的数有没有可能是好数呢?

我不知道,我也不会证明……

不过对于这题来说,由于数据范围不大(也就是10610^6106以内),所以打个表当然是可以的……

这再次告诉了我们打表找规律很重要的道理。

在CGH大佬的讲解下,我终于会证明了……

首先,每个数都可以表示成2kx2^kx2kx的形式,其中xxx为奇数。

接下来我们分类讨论:

当x=1x=1x=1时

我觉得这个不用说……

当k=0k=0k=0时,

首先gcd(x−1,x)=1gcd(x-1,x)=1gcd(x−1,x)=1。

由于xxx为奇数,所以gcd(x−2,x)=1gcd(x-2,x)=1gcd(x−2,x)=1

那么x−1x-1x−1和x−2x-2x−2都与xxx互质,所以等差数列的公差为111。

既然这样,111到x−1x-1x−1都要和xxx互质。

所以xxx自然是质数。

当k>0k>0k>0时,

由于xxx为奇数,所以gcd(x−2,x)=1  gcd(x+2,x)=1gcd(x-2,x)=1 \ \ gcd(x+2,x)=1gcd(x−2,x)=1  gcd(x+2,x)=1

k&gt;0k&gt;0k>0且x&gt;1x&gt;1x>1所以x+2&lt;2kxx+2&lt;2^kxx+2<2kx

x−1x-1x−1和x+1x+1x+1为偶数,和2kx2^kx2kx不互质。显然xxx和2kx2^kx2kx也不互质。

所以公差为444。

首项显然是111,然后我们列出来:1 5 9...

111、555和2kx2^kx2kx互质,没有问题。

可是999呢?

如果2kx2^kx2kx和999互质,那么2kx2^kx2kx必定和333互质,所以不成立!

所以在k&gt;0k&gt;0k>0且x&gt;1x&gt;1x>1时,可以的取值范围在999以内。特别计算一下,只有666符合条件。

综上所述,只有000、222的幂次、质数、666可以为好数。

得证。

再次膜拜一下CGH大爷。

知道了这个结论之后,仔细地再想一想,其实出解还是很容易的。

首先,好数会和模有什么关系?不用想了,反正我们已经打出了表,对一下表,我们就知道它和模似乎没有什么关系。所以,我们考虑暴力。

暴力?不会爆炸吗?

我可以很肯定地告诉你,不会爆炸。

为什么?

有一个很显然的结论:当a&gt;ba&gt;ba>b时,amod&ThinSpace;&ThinSpace;b&lt;=a2a \mod b&lt;=\frac{a}{2}amodb<=2a​

可以感性理解,也可以理性证明:

我们可以分类讨论:

当a≥b&gt;a2a\geq b&gt;\frac{a}{2}a≥b>2a​时,那么amod&ThinSpace;&ThinSpace;b=a−b≤a2a\mod b =a-b\leq \frac{a}{2}amodb=a−b≤2a​

当b≤a2b\leq\frac{a}{2}b≤2a​时,那么amod&ThinSpace;&ThinSpace;b&lt;b≤a2a \mod b&lt;b\leq\frac{a}{2}amodb<b≤2a​

得证。

所以说,在每次操作中,某个数一定会缩小一半或以上。

在没有修改的情况下,xxx能取模lg⁡x\lg xlgx次(当然xxx大于模数)。

我们有一种很巧妙的思想,这种思想其实也不少见了。

有些操作看似暴力,实际上它的操作次数是有限的,所以你可以这么想:

不管操作次数有多少次,反正我最多只做这么多遍,你管我啊?

曾经我用过这个思想切掉了某一道题解说必须用块状链表,而不能用线段树的题目。我用了线段树AC,暴虐题解的感觉真爽……

对于这题,我们可以用线段树维护。

记录区间的好数个数还有最大值。

我们在操作2时,可以先看看这个区间的最大值是多少,如果最大值大于模数,那么就进入这个区间。

这样保证了进入每一个区间都不可能做无用功,所以保证了时间复杂度。

对于每个数,最多被模lg⁡106\lg 10^6lg106次,总共有nnn个数,每次暴力模一个数最多花费lg⁡n\lg nlgn的时间。所以说,不管怎样,你最多就暴力模了nlg⁡nlg⁡106n\lg n \lg 10^6nlgnlg106次。

实际上远远不到这么多,首先数据不会这么狠心地将所有数的价值榨干,其次,在进入一个区间后,处理一些去要处理的数往往是顺路的,也就是说,很难每次暴力下去模都花费lg⁡n\lg nlgn的时间。

可能你会问,修改怎么办?

修改就修改喽,管它呢!

你可以看做将原来的数删去,然后插入一个新的数。原来的数剩余被模的机会没了,新来的数还有一些被模的机会。然而,修改的次数也不多,是有范围的,级别也和nnn差不多吧。就当成是多了这么多个数,实际上时间复杂度也不会被影响。

然后呢,然后呢?当然是没有然后了。

这题就被轻轻松松地解决了。

代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100000
#define MAX 1000000
int n,m;
bool is[MAX+1];
int pri[MAX+1];
int sum[N*4+1],mx[N*4+1];
void build(int,int,int);
int qsum(int,int,int,int,int);
void find(int,int,int,int,int,int);
void change_set(int,int,int,int,int);
int main(){
memset(is,1,sizeof is);
//下面是求质数
for (int i=2;i<=MAX;++i){
if (is[i])
pri[++*pri]=i;
for (int j=1;i*pri[j]<=MAX && j<=*pri;++j){
is[i*pri[j]]=0;
if (i%pri[j]==0)
break;
}
}
//除了质数之外还有0,6,2^i
is[0]=1,is[6]=1;
for (int i=0;1<<i<=MAX;++i)
is[1<<i]=1;
scanf("%d%d",&n,&m);
build(1,1,n);
for (int i=1;i<=m;++i){
int op;
scanf("%d",&op);
if (op==1){
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",qsum(1,1,n,l,r));
}
else if (op==2){
int l,r,mo;
scanf("%d%d%d",&l,&r,&mo);
find(1,1,n,l,r,mo);
}
else{
int x,y;
scanf("%d%d",&x,&y);
change_set(1,1,n,x,y);
}
}
return 0;
}
void build(int k,int l,int r){
if (l==r){
scanf("%d",&mx[k]);
sum[k]=is[mx[k]];
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
sum[k]=sum[k<<1]+sum[k<<1|1];
mx[k]=max(mx[k<<1],mx[k<<1|1]);
}
int qsum(int k,int l,int r,int st,int en){
if (l==st && r==en)
return sum[k];
int mid=l+r>>1;
if (en<=mid)
return qsum(k<<1,l,mid,st,en);
if (mid<st)
return qsum(k<<1|1,mid+1,r,st,en);
return qsum(k<<1,l,mid,st,mid)+qsum(k<<1|1,mid+1,r,mid+1,en);
}
void find(int k,int l,int r,int st,int en,int mo){
if (mx[k]<mo)//如果没有可以被模的数,那就不要继续了
return;
if (l==r){
sum[k]=is[mx[k]%=mo];//暴力修改……
return;
}
int mid=l+r>>1;
if (en<=mid)
find(k<<1,l,mid,st,en,mo);
else if (mid<st)
find(k<<1|1,mid+1,r,st,en,mo);
else{
find(k<<1,l,mid,st,mid,mo);
find(k<<1|1,mid+1,r,mid+1,en,mo);
}
sum[k]=sum[k<<1]+sum[k<<1|1];
mx[k]=max(mx[k<<1],mx[k<<1|1]);
}
void change_set(int k,int l,int r,int x,int c){
if (l==r){
sum[k]=is[mx[k]=c];
return;
}
int mid=l+r>>1;
if (x<=mid)
change_set(k<<1,l,mid,x,c);
else
change_set(k<<1|1,mid+1,r,x,c);
sum[k]=sum[k<<1]+sum[k<<1|1];
mx[k]=max(mx[k<<1],mx[k<<1|1]);
}

总结

首先,打表找规律是一样好东西。

在遇到什么看似是奇葩数论题的时候,如果推不出来,就打表找规律。

要知道有时候找规律比推式子简单多了。

还有,以后见到一些不怎么好维护的东西,就要联想到类似的方法。

如果操作是不可逆的东西,那就试着暴力搞。

反正怎么搞顶多这么多次,那就暴力呗!

JZOJ100045 【NOIP2017提高A组模拟7.13】好数的更多相关文章

  1. [JZOJ100043] 【NOIP2017提高A组模拟7.13】第K小数

    Description 有两个正整数数列,元素个数分别为N和M.从两个数列中分别任取一个数相乘,这样一共可以得到N*M个数,询问这N*M个数中第K小数是多少. Input 输入文件包含三行. 第一行为 ...

  2. JZOJ 100029. 【NOIP2017提高A组模拟7.8】陪审团

    100029. [NOIP2017提高A组模拟7.8]陪审团 Time Limits: 1000 ms  Memory Limits: 131072 KB  Detailed Limits   Got ...

  3. JZOJ 5328. 【NOIP2017提高A组模拟8.22】世界线

    5328. [NOIP2017提高A组模拟8.22]世界线 (File IO): input:worldline.in output:worldline.out Time Limits: 1500 m ...

  4. JZOJ 5329. 【NOIP2017提高A组模拟8.22】时间机器

    5329. [NOIP2017提高A组模拟8.22]时间机器 (File IO): input:machine.in output:machine.out Time Limits: 2000 ms M ...

  5. JZOJ 5307. 【NOIP2017提高A组模拟8.18】偷窃 (Standard IO)

    5307. [NOIP2017提高A组模拟8.18]偷窃 (Standard IO) Time Limits: 1000 ms Memory Limits: 262144 KB Description ...

  6. JZOJ 5286. 【NOIP2017提高A组模拟8.16】花花的森林 (Standard IO)

    5286. [NOIP2017提高A组模拟8.16]花花的森林 (Standard IO) Time Limits: 1000 ms Memory Limits: 131072 KB Descript ...

  7. JZOJ 5305. 【NOIP2017提高A组模拟8.18】C (Standard IO)

    5305. [NOIP2017提高A组模拟8.18]C (Standard IO) Time Limits: 1000 ms Memory Limits: 131072 KB Description ...

  8. 【NOIP2017提高A组模拟9.17】信仰是为了虚无之人

    [NOIP2017提高A组模拟9.17]信仰是为了虚无之人 Description Input Output Sample Input 3 3 0 1 1 7 1 1 6 1 3 2 Sample O ...

  9. 【NOIP2017提高A组模拟9.17】猫

    [NOIP2017提高A组模拟9.17]猫 题目 Description 信息组最近猫成灾了! 隔壁物理组也拿猫没办法. 信息组组长只好去请神刀手来帮他们消灭猫.信息组现在共有n 只猫(n 为正整数) ...

随机推荐

  1. QT安装以及使用(QT支持linux和windows,也支持C/C++代码的编译运行,比vs简洁多)

    Windows: 0. QT Versionqt-win-opensource-4.7.4-mingwqt-creator-win-opensource-2.4.1 1. 系统Windows 7 &a ...

  2. C++——运算符重载

    运算符重载编程基础 例如: //全局函数 完成 +操作符 重载  Complex operator+(Complex &c1, Complex &c2) //类成员函数 完成 -操作符 ...

  3. (20)Oracle函数

    substr 截取字段 substr(字符串,截取开始位置,截取长度) substr(str,n,m) 第二,三参数可以省略, 第二个参数为负数时表示从倒数第n位开始向后截取m个 round(str, ...

  4. 《DSP using MATLAB》Problem 8.34

    今天下了小雨,空气中泛起潮湿的味道,阴冷的感觉袭来,心情受到小小影响. 代码: hp2lpfre子函数 function [wpLP, wsLP, alpha] = hp2lpfre(wphp, ws ...

  5. USB电扇无刷电机改装

    现在USB电扇已经很常见了,网上随便可以低价买到.里面的电机分为有刷和无刷两种.我拆过的有刷USB电扇都非常劣质,里面的电机貌似是旧DVD机的拆机货:而无刷也有优劣之分,有的硅钢片非常少,铜线也细.这 ...

  6. XCode的依赖库管理工具——CocoaPods

    安装CocoaPods 首先,在启动台里打开终端(terminal),并在终端里输入“sudo gem install cocoapods”,按回车后输入电脑用户密码(注意:输入期间不会显示“***” ...

  7. maven 运行run as maven build的时候报错

    eclipse中使用maven插件的时候,运行run as maven build的时候报错 -Dmaven.multiModuleProjectDirectory system propery is ...

  8. UBOOT把文件写入 NandFlash

    如果把一个传到内存中的文件写入到 Nand Flash 中, 如:新的 uboot.bin, zImage(内核), rootfs 等, 如果做呢?我们可以用 Nand Flash 命令来完成. 但是 ...

  9. Caffe系列1——网络文件和求解分析

    1. 首先,我们先看一个完整的文件:lenet_train_test.prototxt name: "LeNet" #整个网络的名称 layer { #数据层——训练数据 name ...

  10. Echart使用过的属性总结

    改变坐标轴颜色与粗细: axisLine: { lineStyle: {//设置轴的颜色 color: '#CD0000', width: 1,//轴的宽度 } } 改变坐标轴上刻度的间隔与倾斜方向: ...