2019ICPC亚洲区域赛 (银川站) G Pot!! 【线段树区间修改+区间查询】

Description

Little Q is very sleepy, and he really needs some coffee to make him awake. At this time, Little L brings a pot to Little Q, and he states the pot as follows.

For a prime number p, if p^m | n and p^{m+1}\not | n, we say \text{pot}_p(n)=m.

The pot is very special that it can make everyone awake immediately.

Now Little L provides (1 \le n \le 10^5) integers a_1, a_2, \cdots, a_n​ to Little Q, each of which is 1 initially. After that, Little L shows 2 types of queries:

  • MULTIPLY l r x : For every i∈[l,r] (1≤l≤r≤n), multiply a_i​ by x (2 \le x \le 10).

  • MAX l r : Calculate the value of

    \displaystyle \max_{l\le i\le r} \left\{ \max_{p|a_i} \left\{ \text{pot}_p (a_i) \right\} \right\}~(1 \le l \le r \le n),

where p is prime.

Now you need to perform q(1 \le q \le 10^5) queries of these two types of queries described above.

If you perform a “MULTIPLY” query, you don't need to output anything.

If you perform a “MAX” query, you need to output a line like ANSWER y, where y the value you've calculated.

Input 

The first line contains two integers n(1 \le n \le 10^5) and q(1 \le q \le 10^5), the number of integers and the number of queries.

Each of the next q lines contains one type of query described above.

Output

 For each “MAX” query, output one line in the format of ANSWER y, where y the value you have calculated.

样例输入

5 6
MULTIPLY 3 5 2
MULTIPLY 2 5 3
MAX 1 5
MULTIPLY 1 4 2 
MULTIPLY 2 5 5 
MAX 3 5

样例输出

ANSWER 1
ANSWER 2

样例解释

If m and n are non-zero integers, or more generally, non-zero elements of an integral domain, it is said that m divides n if there exists an integer k, or an element k of the integral domain, such that m \times k=n, and this is written as m \mid n.

 题目大意:

给定初始值为1的n个数,执行以下两种操作:

1、将 [l,r] 区间内的整数都乘以x

2、询问 [l,r] 区间内的一个极值,即                    

\displaystyle \max_{l\le i\le r} \left\{ \max_{p|a_i} \left\{ \text{pot}_p (a_i) \right\} \right\}~(1 \le l \le r \le n)

首先,理解一下   \displaystyle \max_{p|a_i} \left\{ \text{pot}_p (a_i) \right\} \right\}

这其实是把 a_i 进行了整数的素数幂分解,而  \displaystyle \max_{p|a_i} \left\{ \text{pot}_p (a_i) \right\} \right\}  求的是分解后各个素数的幂中最大次数。

于是,\displaystyle \max_{l\le i\le r} \left\{ \max_{p|a_i} \left\{ \text{pot}_p (a_i) \right\} \right\}~(1 \le l \le r \le n)  也就好理解了,就是对某个区间求上述最大值的最大值。

要注意的是,这里的整数都是从1开始,通过乘上某些x得到的,而x的范围被规定为 [2,10],那么进行整数的素数幂分解后,出现的素数只可能是2、3、5、7,也就是说我们每次要求的实际上就是2、3、5、7的幂次中最大的那个。

由于涉及到区间更新和区间查询,我们可以对2、3、5、7分别构造线段树,记录每个区间内它们的最大幂次。

另外,要注意对2-10的每个数字,要根据他们的素因子去更新对应的树,比如6,就需要更新2、3对应的线段树。

具体解释见代码。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <set>
#include <cstdio> 
#include <map> 
#include <iomanip>
 
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
//#define INF 0x3f3f3f3f
 
using namespace std;
 
const int maxn=1e5+5;
const ll INF=0x3f3f3f3f3f3f3f3f;
 
struct segtree{
	int l,r; 
	ll maxx;
	ll tag; 	//延迟标记	
};
 
segtree t[5][maxn*4];//1对应素因子2的线段树,2--3,3--5,4--7
 
void pushup(int index,int p){//区间合并,向上拓展 
	t[index][p].maxx=max(t[index][2*p].maxx,t[index][2*p+1].maxx);
}
 
void pushdown(int index,int p){//将父节点的状态向下传递(延迟标记的传递) 
	if(t[index][p].tag){//说明父节点的状态已被改变 
		int lson=p*2,rson=p*2+1;
		int mid=(t[index][p].l+t[index][p].r)/2;
		t[index][lson].tag+=t[index][p].tag;		//注意这里一定是要累加,因为该区间有可能被父节点多次更新 
		t[index][rson].tag+=t[index][p].tag;
		t[index][lson].maxx+=t[index][p].tag;
		t[index][rson].maxx+=t[index][p].tag;
		t[index][p].tag=0;//父节点传递完成后标志复原 
	}
}
 
void build(int index,int p,int l,int r){//建树 
	int mid=(l+r)/2;
	t[index][p].l=l;
	t[index][p].r=r;
	t[index][p].tag=0;	
	t[index][p].maxx=0;
	if(l==r){
		return;
	}
	build(index,p*2,l,mid);
	build(index,2*p+1,mid+1,r);
	pushup(index,p);
}
 
ll query(int index,int p,int L,int R){
	if(L<=t[index][p].l&&R>=t[index][p].r){
		return t[index][p].maxx;
	}
    pushdown(index,p);//注意在向下查询前,要先下推
	int lson=p*2;
	int rson=p*2+1;
	ll tmax=-INF;
	int mid=(t[index][p].l+t[index][p].r)/2;
	if(L<=mid){		//如果左侧还有区间 
		tmax=max(tmax,query(index,lson,L,R));
	}
	if(R>mid){ 		//如果右侧还有区间
		tmax=max(tmax,query(index,rson,L,R));
	} 
	return tmax;
} 
 
 
 
void update(int index,int p,int L,int R,ll num){
	int lson=p*2,rson=p*2+1;
	if(L<=t[index][p].l&&R>=t[index][p].r){//区间被完全覆盖 
		t[index][p].tag+=num;		//这里累加,原因同上 
		t[index][p].maxx+=num;
		return;//由于此处直接返回,所以需要延迟标记,此处未更新该节点的子树 
	}
	pushdown(index,p);//进入子节点前先传递延迟标记,便于接下来的递归更新 
	int mid=(t[index][p].l+t[index][p].r)/2;
	if(L<=mid){
		update(index,lson,L,R,num);
	} 
	if(R>mid){
		update(index,rson,L,R,num);
	}
	pushup(index,p);//子节点更新返回后要更新父节点 
}
 
int main(){
	int n,q;
	scanf("%d%d",&n,&q);
	for(int i=1;i<=4;i++){
		build(i,1,1,n);
	} 
	char ch[20];
	for(int i=1;i<=q;i++){
		scanf("%s",ch);
		if(ch[1]=='U'){
			int a,b;
			ll c;
			scanf("%d%d%lld",&a,&b,&c);
            //处理2-10的每个数字,更新相应的树
			if(c==6){
				update(1,1,a,b,1);
				update(2,1,a,b,1);
			}
			else if(c==10){
				update(1,1,a,b,1);
				update(3,1,a,b,1);
			}
			else if(c%2==0){
				if(c==2){
					update(1,1,a,b,1);
				}
				else if(c==4){
					update(1,1,a,b,2);//4中包含2的2次幂,所以要加2
				}
				else{
					update(1,1,a,b,3);//8中包含2的3次幂,所以要加3
				}
			}
			else if(c%3==0){
				if(c==3){
					update(2,1,a,b,1);
				}
				else{
					update(2,1,a,b,2);//9中包含3的2次幂,所以要加2
				}
			}
			else if(c%5==0){
				update(3,1,a,b,1);
			}
			else{
				update(4,1,a,b,1);
			}
		}
		else{
			int a,b;
			scanf("%d%d",&a,&b);
			ll ans=-INF,tmp;
			for(int i=1;i<=4;i++){
				ans=max(ans,query(i,1,a,b));//依次比较取该区间中2、3、5、7的幂的最大值
			}
			printf("ANSWER %lld\n",ans);
		}
	} 
	return 0;
}
发布了30 篇原创文章 · 获赞 5 · 访问量 900

猜你喜欢

转载自blog.csdn.net/qq_42840665/article/details/103430301
今日推荐