Concurso regional asiático ICPC 2019 (estación de Yinchuan) G Pot !! [Modificación del intervalo del árbol del segmento de línea + consulta de intervalo]

Descripción

El pequeño Q tiene mucho sueño y realmente necesita un poco de café para despertarlo. En este momento, Little L trae un bote a Little Q, y declara el bote de la siguiente manera.

Para un número primo p, si  p ^ m |  norte y  p ^ {m + 1} \ not |  norte, decimos  \ text {pot} _p (n) = m.

El bote es muy especial, ya que puede despertar a todos de inmediato.

Ahora Little L proporciona 1 \ le n \ le 10 ^ 5enteros ( a_1, a_2, \ cdots, a_na Little Q, cada uno de los cuales es 1 inicialmente. Después de eso, Little L muestra 2 tipos de consultas:

  • MULTIPLICAR lrx  : por cada i∈ [l, r] (1≤l≤r≤n), multiplique  aipor x ( 2 \ le x \ le 10).

  • MAX lr  : Calcular el valor de

    \ 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),

     

donde p es primo

Ahora debe realizar 1 \ le q \ le 10 ^ 5consultas q ( ) de estos dos tipos de consultas descritas anteriormente.

Si realiza una consulta " MULTIPLY ", no necesita generar nada.

Si realiza una consulta " MAX ", debe generar una línea como  ANSWER y, donde y el valor que ha calculado.

Entrada 

La primera línea contiene dos enteros n ( 1 \ le n \ le 10 ^ 5) y q ( 1 \ le q \ le 10 ^ 5), el número de enteros y el número de consultas.

Cada una de las siguientes líneas q contiene un tipo de consulta descrita anteriormente.

Salida

 Para cada consulta " MAX ", envíe una línea en el formato de  ANSWER y, donde y el valor que ha calculado.

Entrada de muestra

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

Salida de muestra

ANSWER 1
ANSWER 2

Explicación de la muestra

Si myn son enteros distintos de cero, o más generalmente, elementos distintos de cero de un dominio integral, se dice que m divide n si existe un entero k, o un elemento k del dominio integral, de modo que  m \ veces k = n, y esto se escribe como  m \ mid n.

 La idea principal:

Dados n números con un valor inicial de 1, realice las siguientes dos operaciones:

1. Multiplica todos los enteros en el intervalo [l, r] por x

2. Solicite un valor extremo en el intervalo [l, r], a saber                    

\ 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)

Primero, comprende   \ displaystyle \ max_ {p | a_i} \ left \ {\ text {pot} _p (a_i) \ right \} \ right \}

De hecho, esto es para aiser un primer número entero poderes de la descomposición, mientras  \ displaystyle \ max_ {p | a_i} \ left \ {\ text {pot} _p (a_i) \ right \} \ right \}  que buscan el máximo número es una potencia de cada uno de descomposición de los números primos.

Por \ 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)  lo tanto, es fácil de entender, es decir, encontrar el valor máximo del valor máximo para un determinado intervalo.

Cabe señalar que los enteros aquí comienzan desde 1 y se obtienen al multiplicar algo de x, y el rango de x se especifica como [2,10], luego, después de la descomposición de potencia prima del entero, los números primos que aparecen solo son posibles Es 2,3,5,7, lo que significa que cada vez que solicitamos es en realidad la mayor de las potencias de 2,3,5,7.

Como implica la actualización de intervalos y la consulta de intervalos, podemos construir árboles de segmentos de línea para 2, 3, 5, 7, respectivamente, y registrar su potencia máxima en cada intervalo.

Además, debe tenerse en cuenta que para cada número de 2-10, el árbol correspondiente debe actualizarse de acuerdo con sus factores primos, como 6, debe actualizar el árbol de líneas correspondiente a 2, 3.

Vea el código para una explicación específica.

#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 artículos originales publicados · ganó 5 · 900 visitas

Supongo que te gusta

Origin blog.csdn.net/qq_42840665/article/details/103430301
Recomendado
Clasificación