100045. 【NOIP2017提高A组模拟7.13】好数

题目描述

我们定义一个非负整数是“好数”,当且仅当它符合以下条件之一:
1.这个数是0或1
2.所有小于这个数且与它互质的正整数可以排成一个等差数列例如,8就是一个好数,因为1,3,5,7排成了等差数列。
给出N个非负整数 a i a_i ,然后进行如下三个操作 m m 次:
1.询问区间[L,R]有多少个好数
2.将区间[L,R]内所有数对S取余(S≤1000000)
3.将第C个数更改为X

数据范围

n , m 1 0 5 , a i 1 0 6 n,m \le 10^5,a_i \le 10^6

题目分析

这道题其实就是强行地把两个问题并在一起
Q1:如何求好数
Q2:如何进行那三个操作
我们一个一个来解决

Q1
  • 通过各种方法(打表),我们可以得出 一个非负整数x是好数当且仅当:
  1. 它是0或1或6
  2. 它是一个质数
  3. 它是2的非负次幂
  • 证明留给大家思考(懒癌发作)
  • 可以去别的博客看一下
Q2
  • 这才是本题的关键(吗?)
  • 第一个和第三个都可以直接用线段树快速搞定
  • 那么第二种操作如何实现呢?
  • 其实细想一下我们就可以发现一个东西,对于任意的非负整数 a , b a,b ,如果 a b a \ge b ,则必有 a m o d    b < a 2 a \mod b < \frac {a}{2}
  • 分类讨论之后结论显然
  • 所以我们发现,对于膜一个数s,如果当前数小于s,那么修改是多余的。
  • 所以去掉多余的修改,每一个数做多被修改 l o g log 次。
  • 所以总的修改次数就是 i = 1 n l o g 2 a i \sum_{i=1}^{n}log_2{a_i}
  • 用线段树暴力判断,如果当前区间最大值小于s,就不用进这个区间。
  • 暴力修改即可,时间复杂度为 O ( l o g 2 n i = 1 n l o g 2 a i ) O(log_2n*\sum_{i=1}^{n}log_2{a_i})

代码

#include<cstdio>
#include<cstring>
using namespace std;
const int max=1000000;
bool bk[1100000];int a[110000];
struct node{
	int l,r,lc,rc,c,max;
}tr[210000];int len=0;
int mymax(int x,int y) {return x>y?x:y;}
void build(int l,int r){
	int now=++len;
	tr[now].l=l;tr[now].r=r;
	tr[now].lc=tr[now].rc=-1;
	if(l<r){
		int mid=(l+r)/2;
		tr[now].lc=len+1;build(l,mid);
		tr[now].rc=len+1;build(mid+1,r);
	}
}
void change1(int now,int k,int c){
	if(tr[now].l==tr[now].r){
		tr[now].max=c;
		if(bk[c]) tr[now].c=1;else tr[now].c=0;
		return ;
	}
	int mid=(tr[now].l+tr[now].r)/2;
	int lc=tr[now].lc,rc=tr[now].rc;
	if(k<=mid) change1(lc,k,c);
	else change1(rc,k,c);
	tr[now].c=tr[lc].c+tr[rc].c;
	tr[now].max=mymax(tr[lc].max,tr[rc].max);
}
void change2(int now,int l,int r,int c){
	if(tr[now].l==tr[now].r){
		int t=tr[now].max%c;
		tr[now].max=t;if(bk[t]) tr[now].c=1;else tr[now].c=0;
		return ;
	}
	int mid=(tr[now].l+tr[now].r)/2;
	int lc=tr[now].lc,rc=tr[now].rc;
	if(r<=mid) {if(tr[lc].max>=c) change2(lc,l,r,c);}
	else if(mid<l) {if(tr[rc].max>=c) change2(rc,l,r,c);}
	else{
		if(tr[lc].max>=c) change2(lc,l,mid,c);
		if(tr[rc].max>=c) change2(rc,mid+1,r,c);
	}
	tr[now].c=tr[lc].c+tr[rc].c;
	tr[now].max=mymax(tr[lc].max,tr[rc].max);
}
int findans(int now,int l,int r){
	if(tr[now].l==l&&tr[now].r==r) return tr[now].c;
	int mid=(tr[now].l+tr[now].r)/2;
	int lc=tr[now].lc,rc=tr[now].rc;
	if(r<=mid) return findans(lc,l,r);
	else if(mid<l) return findans(rc,l,r);
	else return findans(lc,l,mid)+findans(rc,mid+1,r);
}
int main()
{
	int n,m;scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=0;i<=max;i++) bk[i]=true;
	for(int i=2;i<=max;i++){
		if(bk[i])
			for(int j=i;j<=max/i;j++) bk[i*j]=false;
	}
	int d=2;bk[6]=true;
	while(d<=max) bk[d]=true,d*=2;
	if(n*m<=1000000){
		for(int i=1;i<=m;i++){
			int id;scanf("%d",&id);
			if(id==1){
				int l,r,sum=0;scanf("%d%d",&l,&r);
				for(int j=l;j<=r;j++) if(bk[a[j]]) sum++;
				printf("%d\n",sum);
			}
			else if(id==2){
				int l,r,s;scanf("%d%d%d",&l,&r,&s);
				for(int j=l;j<=r;j++) a[j]%=s;
			}
			else if(id==3){
				int c,s;scanf("%d%d",&c,&s);
				a[c]=s;
			}
		}
		return 0;
	}
	build(1,n);
	for(int i=1;i<=n;i++) change1(1,i,a[i]);
	for(int i=1;i<=m;i++){
		int id;scanf("%d",&id);
		if(id==1){
			int l,r;scanf("%d%d",&l,&r);
			printf("%d\n",findans(1,l,r));
		}
		else if(id==2){
			int l,r,s;scanf("%d%d%d",&l,&r,&s);
			change2(1,l,r,s);
		}
		else if(id==3){
			int c,s;scanf("%d%d",&c,&s);
			change1(1,c,s);
		}
	}
	return 0;
}
发布了58 篇原创文章 · 获赞 12 · 访问量 8567

猜你喜欢

转载自blog.csdn.net/fengqiyuka/article/details/85028809
今日推荐