Herramientas de usuario

Herramientas del sitio


algoritmos-oia:problemas-con-queries:offline-vs-online

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Próxima revisión
Revisión previa
algoritmos-oia:problemas-con-queries:offline-vs-online [2018/05/04 06:51]
sebach creado
algoritmos-oia:problemas-con-queries:offline-vs-online [2018/10/08 17:00] (actual)
santo
Línea 7: Línea 7:
  
  
-PROBLEMAS+[[http://​codeforces.com/​problemset/​problem/​932/​B|Recursive Queries]]: $g(n)$ es el resultado de multiplicar los dígitos de $n$ distintos de $0$, hasta tener un número de $1$ cifra. Te dan $Q\leq 2*10^5$ queries, con $l,r\leq 10^6 ,k<10$. Hay que decir cuántos enteros $l \leq x \leq r$ existen tales que $g(x)=k$.
  
  
 +[[http://​codeforces.com/​problemset/​problem/​276/​C|Maxima suma de queries]]: Tenés un array $a$ de $n \leq 10^5$ enteros, y te van a dar $q \leq 10^5$ queries, con dos números $1 \leq l_i \leq r_i \leq n$, y te piden la suma de $a[l_i]+a[l_i+1]+...+a[r_i]$. Vas a tener que acomodar el array al principio, para que la suma total de los resultados de la query sea máxima.
  
-Con las queries, hay que tener cuidado con cuántas hay. Si hay muchas queries (del orden de $10^5 van a ser en general, más no porque hay que leer cada una), y las queries consisten de varios números, o palabras, hay que tener mucho cuidado con el método de input/​output utilizado. Habrá que usar el método más rápido posible para que esto no sea un problema. ​FIXME link a i/o+ 
 +[[http://​codeforces.com/​problemset/​problem/​713/​A|Sonya and Queries]]: Te van dando queries de agregar y sacar números, y cada tanto te preguntan cuántos números tenés actualmente que matcheen con un patrón dado. 
 + 
 + 
 +[[http://​codeforces.com/​contest/​381/​problem/​E|Sereja and Brackets]](difícil):​ Te dan una string $s$ de paréntesis y te dan varias queries, que consisten en dos enteros $l, r$. Hay que responder la máxima longitud de una subsecuencia bien parenteseada de la substring $s[l]s[l+1] ... s[r]$. 
 + 
 + 
 + 
 +Con las queries, hay que tener cuidado con cuántas hay. Si hay muchas queries (del orden de $10^5van a ser en general, más no porque hay que leer cada una, es decir no podrías ni siquiera leer $10^9$ queries), y las queries consisten de varios números, o palabras, hay que tener mucho cuidado con el método de input/​output utilizado. Habrá que usar el método más rápido posible para que esto no sea un problema. ​Para eso pueden leer este [[algoritmos-oia/input-output|link]].
  
 Una vez leídas las queries, hay que responderlas. Una vez leídas las queries, hay que responderlas.
Línea 21: Línea 30:
 Si es el escenario está siempre fijo, no importa cuándo nos den una query, ni cuándo la respondamos,​ la respuesta siempre será la misma. Si es el escenario está siempre fijo, no importa cuándo nos den una query, ni cuándo la respondamos,​ la respuesta siempre será la misma.
 Aprovechando esto, podremos por ejemplo, al recibir todas las queries, ordenarlas, y en base a eso elaborar un algoritmo eficiente que resuelva el problema con las queries ordenadas. Aprovechando esto, podremos por ejemplo, al recibir todas las queries, ordenarlas, y en base a eso elaborar un algoritmo eficiente que resuelva el problema con las queries ordenadas.
-Un algoritmo conocido para estos casos es el algoritmo de Mo. FIXME link +Un algoritmo conocido para estos casos es el [[algoritmos-oia/​problemas-con-queries/​algoritmo-de-mo|algoritmo de Mo]].
  
-El problema de arriba de ......... por ejemplo, ​puede resolverse con este algoritmo. Veámoslo.+El problema de arriba de "​Recursive Queries"​ es un ejemplo ​de queries Offlinemientras que "Sonya and Queries"​ no, porque te van cambiando el escenario y en el medio te hacen preguntas sobre el estado actual.
  
-CODIGO+Acá dejo el código de "​Recursive Queries",​ sin ningún algoritmo loco. Simplemente,​ una idea copada también para estas queries es, me piden cuántos números entre $l$ y $r$ hay. Puedo guardar información de cuántos hay entre $1$ y $x$ para todo $x$, y luego responder "​cuántos hay entre $1$ y $r$ menos cuántos hay entre $1$ y $l-1$. 
 +Como $k$ es chico, puedo guardar en $cuantos[i][k]$ cuántos $x$ hay entre $1$ e $i$ cuya función $g(x)=k$. Tomaría del orden de $O(10*N)$ completar los estados, y luego respondo las queries en $O(1)$. 
 + 
 +<code cpp queries.cpp>​ 
 + 
 +#​include<​bits/​stdc++.h>​ 
 +using namespace std; 
 +#define forn(i,n) for(int i=0;​i<​(int)(n);​ i++) 
 +#define forsn(i,​s,​n) for(int i=(s);​i<​(int)(n);​ i++) 
 +#define pb push_back 
 + 
 +int maxn=1000003;​ 
 +vector<​int>​ ans(maxn);​ 
 + 
 +int main(){ 
 + forn(i, 10)ans[i]=i;​ 
 + forsn(i, 10, maxn){ 
 + int pro=1; 
 + int nc=i; 
 + while(nc){ 
 + int l=nc%10; 
 + if(l)pro*=l;​ 
 + nc/​=10;​ 
 +
 +                // calculo g(i) dinamicamente usando que el producto de los digitos siempre es menor que el numero 
 + ans[i]=ans[pro];​ 
 +
 + vector< vector<​int>​ > cant; 
 + vector<​int>​ dec(10, 0); 
 + cant.pb(dec);​ // para que si l=1, cant[l-1][k]=cant[0][k] de 0 para cualquier k 
 + forsn(i, 1, maxn){ 
 + dec.clear();​ 
 + forn(j, 10){ 
 + dec.pb(cant[i-1][j]);​ 
 +
 + dec[ans[i]]++;​ 
 + cant.pb(dec);​ 
 +
 + int n; 
 + cin>>​n;​ 
 + while(n--){ 
 + int l, r, k; 
 + cin>>​l>>​r>>​k;​ 
 + cout<<​cant[r][k]-cant[l-1][k]<<"​\n";​ 
 +
 +
 + 
 +</​code>​
  
 ==== Queries online ==== ==== Queries online ====
  
-Hay problemas, en cambio, que pueden hacer variar nuestro escenario con las queries. Por ejemplo, puede haber dos tipos de queryuno que nos pida una respuesta a alguna pregunta, y otra que modifique algo, por ejemplo que agregue un número a un conjunto, o lo elimine+Hay problemas, en cambio, que pueden hacer variar nuestro escenario con las queries, ​como "Sonya and Queries"​
-En estos casos, tendremos que resolver el problema con algo más elaborado que ordenando las queries al final, ya que la respuesta dependerá de en qué momento se efectuó cada query.+En estos casos, tendremos que resolver el problema con algo más elaborado que ordenando las queries al final o precalculando todo al principio como hicimos en el código de "​Recursive Queries"​, ya que la respuesta dependerá de en qué momento se efectuó cada query. 
 + 
 +La estructura más útil y más usada para este tipo de problemas (y también para algunos offline) es el [[algoritmos-oia/​estructuras/​segment-tree|Segment Tree]]. 
 + 
 +Por ejemplo, el problema "​Sereja and Brackets"​ de arriba, vamos a resolverlo con Segment Tree. (Leer primero ese post para entender la solución.) 
 + 
 +FIXME Describir solucion, nodo, como mergear 
 + 
 + 
 +<code cpp sereja-and-brackets.cpp>​ 
 +#​include<​bits/​stdc++.h>​ 
 + 
 +using namespace std; 
 + 
 +#define forn(i,n) for(int i=0;​i<​(int)(n);​ i++) 
 +#define forsn(i,​s,​n) for(int i=(s);​i<​(int)(n);​ i++) 
 +#define pb push_back 
 + 
 +struct node{ 
 + int a,b,c; 
 +}; 
 + 
 +vector<​char>​ arr; 
 +vector<​node>​ rmq; 
 +vector<​int>​ cant; 
 + 
 +char NEUTRO='​(';​ 
 + 
 +node oper(node x, node y){ 
 + int t=min(x.b, y.c); 
 + node ret; 
 + ret.a=x.a+y.a+t;​ 
 + ret.b=x.b+y.b-t;​ 
 + ret.c=x.c+y.c-t;​ 
 + return ret; 
 +
 + 
 +node res(int nodo, int l, int r, int i, int j){ 
 + node n; 
 + n.a=0; 
 + n.b=0; 
 + n.c=0; 
 + if(j<=l || i>​=r)return n; 
 + if(i<=l && j>=r){ 
 + return rmq[nodo];​ 
 +
 + return oper(res(nodo*2,​ l, (l+r)/2, i, j), res(nodo*2+1,​ (l+r)/2, r, i, j)); 
 +
  
-Estructuras que pueden resultar útiles para este tipo de problemas son FIXME linkssss!+void build(){ 
 + node vacio; 
 + for(int i=0; i<​2*(int)(arr.size());​ i++){ 
 + rmq.push_back(vacio);​ 
 + cant.push_back(0);​ 
 +
 + for(int i=(int)(arr.size());​ i<​2*(int)(arr.size());​ i++){ 
 + node nuevo; 
 + nuevo.a=0;​ 
 + if(arr[i-(int)arr.size()]=='​('​){ 
 + nuevo.b=1;​ 
 + nuevo.c=0;​ 
 + }else{ 
 + nuevo.c=1;​ 
 + nuevo.b=0;​ 
 +
 + rmq[i]=nuevo;​ 
 + cant[i]=1;​ 
 +  
 +
 + for(int i=(int)arr.size()-1;​ i>=1; i--){ 
 + rmq[i]=oper(rmq[2*i],​ rmq[2*i+1]);​ 
 + cant[i]=cant[2*i]+cant[2*i+1];​ 
 +
 +
 +
  
-Por ejemplo, en el problema de .......... de arriba:+int main(){ 
 + string s; 
 + while(cin>>​s){ 
 + rmq.clear(); 
 + arr.clear();
  
-Solución + int n=s.size(); 
-Código+ forn(i, s.size()){ 
 + arr.pb(s[i]);​ 
 +
 + int p2=1; 
 + while(p2<​n)p2*=2;​ 
 + while(p2>​n){ 
 + arr.push_back(NEUTRO);​ 
 + p2--; 
 +
 + int si=(int)arr.size();​ 
 + build();​ 
 +  
 + // get ans of range [a,b) : res(1,​0,​si,​a,​b) -> con a=0,​...,​n-1 
 + int q; 
 + cin>>​q;​ 
 + while(q--){ 
 + int a,b; 
 + cin>>​a>>​b;​ 
 + a--; 
 + node an=res(1, 0, si, a, b); 
 + cout<<​2*an.a<<​endl;​ 
 +
 + return 0; 
 +
 +
 +</​code>​
algoritmos-oia/problemas-con-queries/offline-vs-online.1525416677.txt.gz · Última modificación: 2018/05/04 06:51 por sebach