Codeforces Round # 672 (Div.2) Solución del problema de AD

Codeforces Round # 672 (Div.2) Solución del problema de AD

// Escriba el valor de calificación 1987/2184

Enlace del concurso: https://codeforces.com/contest/1420 Pregunta
A,
relacionada con la clasificación, pregunta sobre el agua del pensamiento

El título significa una longitud dada n número de columnas, puede preguntar en n × \ vecesDespués del número de intercambios × (n-1) / 2-1, la secuencia se ordena según el valor.

No entiendo muy bien por qué algunas personas intentan pensar en
el algoritmo de clasificación basado en el subíndice original de la secuencia. En el peor de los casos, el número de intercambios requeridos se acumula de 1 a n-1, y el valor es n × \ veces× (n-1) / 2, y el número máximo de operaciones dado por la pregunta es solo uno menos. Entonces, de hecho, en otras palabras, excepto en el peor de los casos, todas las situaciones se pueden ordenar por intercambio.
Entonces, ¿cuál es el peor de los casos? El peor de los casos es que la secuencia numérica original es estrictamente monótona decreciente. Simplemente juzga directamente.

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        int n;
        cin>>n;
        vector<int>num(n);
        for(auto &x:num) cin>>x;
        bool flag=1;
        for(int i=1;i<n;i++) if(num[i]>=num[i-1]) flag=0;
        if(flag) cout<<"NO"<<endl;
        else cout<<"YES"<<endl;
    }
}


Cálculo del título B , conclusión simple

La pregunta significa que dada una secuencia numérica num [], le preguntará cuántos grupos de subíndices i y j satisfacen i <j, y num [i] & num [j]> = num [i] ^ num [j].

Lo primero que debe darse cuenta es que la operación & y no obtendrá un valor mayor que los dos valores involucrados en la operación. Para un número x, tomando x = 6 como ejemplo, la representación binaria de 6 es 000… 000110. Para todos los ceros del lado izquierdo, es imposible obtener 1 después de la operación & AND con otro número, pero para ^ Para la operación OR exclusivo, si el bit correspondiente en el binario de otro número es 1, la operación ^ OR exclusivo obtendrá un bit 1. En este caso, el resultado de la operación ^ será inevitablemente mayor que el resultado de la operación & AND, por lo que la izquierda Todos los ceros del lado también deben ser todos ceros en binario.
Para el 1 más alto de x = 6, si el bit correspondiente de otro número es 1, entonces el resultado de la operación & y será 1, y el resultado de la operación ^ exclusiva OR será 0, que debe cumplir con los requisitos de. Si el bit correspondiente a otro número es 0, el resultado de la operación & y será 0, y el resultado de la operación ^ XOR será 1, que no debe cumplir los requisitos.

De esto podemos sacar la conclusión de que para el número num [j], puede formar un subíndice i que cumpla con los requisitos del título, y debe satisfacer el bit 1 más alto de num [i] y el bit 1 más alto de num [j]. Por lo tanto, usamos directamente una matriz cas [i] para registrar cuántas veces aparece el número cuyo bit más alto es el i-ésimo bit antes del subíndice j.
Utilice la matriz cas para obtener la respuesta en O (n) tiempo.

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

ll cas[100],sum[100];

int32_t main()
{
    
    
    IOS;
    cas[0]=1;
    for(ll i=1;;i++)
    {
    
    
        cas[i]=cas[i-1]*2;
        if(cas[i]>1e9) break;
    }
    int t;
    cin>>t;
    while(t--)
    {
    
    
        int n;
        cin>>n;
        vector<ll>num(n);
        for(auto &x:num) cin>>x;
        memset(sum,0,sizeof(sum));
        ll ans=0;
        for(int i=0;i<n;i++)
        {
    
    
            int tar=0;
            while(cas[tar]<=num[i]) tar++;
            ans+=sum[tar-1];
            sum[tar-1]++;
        }
        cout<<ans<<endl;
    }
}

La pregunta
C1 de C puede ver fácilmente que se puede escribir dp, pero debido a que C2 necesita intercambiar y cambiar los datos de la matriz, cada vez que se modifica dp O (n), inevitablemente se agotará el tiempo de espera. C2 necesita llegar a una conclusión.

La pregunta significa que dada una secuencia de longitud n, es necesario seleccionar una parte de los subíndices para formar una nueva secuencia. El valor de la nueva secuencia se define como todos los subíndices impares en la nueva secuencia menos todos los subíndices pares. Ahora pregunte cuál es el valor máximo.
En el caso de C2, existen q más operaciones de modificación en la secuencia original. Cada modificación intercambiará las posiciones de dos dígitos en la secuencia original. Para cada modificación se emite el valor máximo de la secuencia que se puede formar.

En el caso de C1, es fácil ver que se puede resolver rápidamente con dp. dp [i] [0] representa el valor máximo que pueden constituir los primeros i dígitos cuando se utiliza el i-ésimo dígito y el último dígito de la nueva secuencia es un subíndice impar, dp [i] [1] representa el i-ésimo El valor máximo que pueden constituir los primeros i dígitos cuando el último número de la nueva secuencia numérica es un subíndice par.
En la ecuación de transferencia, dp [i] [0] se puede dividir en dos transiciones: la selección del i-ésimo dígito y la no selección. Si no se selecciona el i-ésimo dígito, entonces dp [i] [0] comienza desde dp [i-1] [ 0] se transfiere, si se selecciona el número i-ésimo, dp [i] [0] se transfiere de dp [i-1] [1] + num [i], y los dos se toman como máx. La transferencia de dp [i] [1] es la misma.

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const ll maxn=3e5+7;

ll dp[maxn][2];
ll num[maxn];
ll n,q;

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        cin>>n>>q;
        for(ll i=0;i<n;i++)
        {
    
    
            cin>>num[i];
            dp[i][0]=dp[i][1]=-llINF;
        }
        dp[0][0]=num[0];dp[0][1]=0;
        for(ll i=1;i<n;i++)
        {
    
    
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+num[i]);
            dp[i][1]=max(dp[i-1][1],dp[i-1][0]-num[i]);
        }
        cout<<max(dp[n-1][0],dp[n-1][1])<<endl;
    }
}

Para C2, existen q operaciones de intercambio, que intercambiarán las posiciones de los dos números en la secuencia y requerirán que se genere el valor más grande después de cada operación de intercambio. En este punto, es obvio que continuar usando la solución dp de C1 se agotará, por lo que debemos pensar en una solución que sea O (1) u O (logn) después de cada modificación.
Las palabras aquí se resumen y resumen como una regla. Nuestra elección óptima final debe ser el "pico" y el "valle" del valor mediano de esta serie. Si demuestra la forma, puede dar ejemplos como 1, 4, 3, 2, 7 usted mismo. Para el intervalo de 4, 3 y 2, probamos por contradicción que debemos elegir 4 y 2. Utilice este esquema de prueba para probar la conclusión, y no se toma el "valle" al principio y al final de la secuencia.
Para cada modificación e intercambio de los números de subíndices i y j, las posiciones que pueden tener un impacto (generación o desaparición de crestas y valles) son i-1, i, i + 1, j-1, j, j + 1, seis posiciones, Realice el cálculo inverso en el valor de pico y valle original de cada posición y luego vuelva a calcular el pico y el valle formados después de la modificación. Aquí debemos prestar atención a saltar directamente cuando i y j son iguales, y prestar atención a la coincidencia de i + 1 y j-1.

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const ll maxn=3e5+7;

ll num[maxn];
ll n,q;

ll check(ll i)//返回下标i的位置是波峰还是波谷,决定了这个位置上的数字是增加还是减少到最终结果上
//1代表波峰,-1代表波谷,0代表其他情况,恰好对应了其累加到结果上对应的系数
{
    
    
    if(n==1) return 1;//特判数列只有1个数的情况
    if(i==1)//如果当前位置是开头位置,如果是波峰的话就累加到结果上,其他情况都不取(开头的波谷不取)
    {
    
    
        if(num[1]>num[2]) return 1;//此时判断右侧即可
        else return 0;
    }
    if(i==n)//如果当前位置是末尾位置,如果是波峰的话就累加到结果上,其他情况都不取(末尾的波谷不取)
    {
    
    
        if(num[i]>num[i-1]) return 1;//此时判断左侧即可
        else return 0;
    }
    if(num[i]>num[i-1]&&num[i]>num[i+1]) return 1;//其他情况下如果既大于左边又大于右边则为波峰
    if(num[i]<num[i-1]&&num[i]<num[i+1]) return -1;//其他情况下入股既小于左右又小于右边则为波谷
    return 0;
}

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        cin>>n>>q;
        for(ll i=1;i<=n;i++) cin>>num[i];
        ll ans=0;
        for(ll i=1;i<=n;i++)//直接for一遍,每个数乘以其check对应的系数累加到结果上即可
        {
    
    
            ans+=check(i)*num[i];
        }
        cout<<ans<<endl;
        for(ll i=0;i<q;i++)//q次调换num[l]和num[r]
        {
    
    
            ll l,r;
            cin>>l>>r;
            if(l!=r)//l=r的情况直接跳过
            {
    
    
                if(l>1) ans-=check(l-1)*num[l-1];//l-1的位置如果存在,做逆运算
                if(l<n&&l+1!=r) ans-=check(l+1)*num[l+1];//l+1的位置如果存在,也做逆运算
                //下面同样的对r-1,r+1,l,r四个位置做逆运算
                if(r>1&&r-1>l+1) ans-=check(r-1)*num[r-1];//此处要注意特判r-1的位置与l+1不重合,否则会重复运算,l和r差值小于2的情况下都会有重合
                if(r<n) ans-=check(r+1)*num[r+1];
                ans-=check(l)*num[l];
                ans-=check(r)*num[r];
                swap(num[l],num[r]);//交换后把对应受影响位置的值加回来
                if(l>1) ans+=check(l-1)*num[l-1];
                if(l<n&&l+1!=r) ans+=check(l+1)*num[l+1];
                if(r>1&&r-1>l+1) ans+=check(r-1)*num[r-1];
                if(r<n) ans+=check(r+1)*num[r+1];
                ans+=check(l)*num[l];
                ans+=check(r)*num[r];
            }
            cout<<ans<<endl;
        }
    }
}

Pregunta D
La aplicación de la idea de la línea de exploración, pero esto es unidimensional.

El título significa que hay n luces, y cada luz tiene una hora de inicio y una hora de finalización. Ahora debes preguntarte sobre la combinación de una pequeña cantidad de k luces, y todas están encendidas en un momento determinado. .

Esta pregunta es en realidad una aplicación simple de la idea de una línea de exploración de árbol de segmento de línea. Simplemente coloque la hora de inicio y la hora de finalización en la misma matriz, marque la hora de inicio como 1 y marque la hora de finalización como -1. Según el orden del tiempo, directamente por una vez. Cuando se encuentra el tiempo marcado como 1, el número total de luces que pueden estar encendidas al mismo tiempo en el intervalo actual es +1, y cuando el tiempo está marcado como -1, se reduce en 1. Use una matriz num para registrar el número de luces que se pueden encender al mismo tiempo en el intervalo actual. Si num> = k, significa que se puede formar una combinación de k luces que cumpla con los requisitos. Usamos una idea similar a dp. La luz recién agregada debe estar en En esta combinación de k luces, no se superpondrá con la parte calculada previamente, solo agregue el número de combinaciones de k-1 desde num-1 a la respuesta final.

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const ll mod=998244353;
const ll maxn=3e5+7;

ll n,k;
struct Node
{
    
    
    ll data,ope;
};

vector<Node>node;

bool cmp(Node a,Node b)
{
    
    
    if(a.data==b.data) return a.ope>b.ope;//注意这里的cmp,时间点相同的时候,要把增加的点放到减少的点前面
    else return a.data<b.data;
};

ll cas[maxn];

ll qpow(ll a,ll p)
{
    
    
    ll ret=1;
    while(p)
    {
    
    
        if(p&1) ret=ret*a%mod;
        a=a*a%mod;
        p>>=1;
    }
    return ret;
}

ll cal(ll num)
{
    
    
    if(num<k) return 0;
    return cas[num-1]*qpow(cas[num-k]*cas[k-1]%mod,mod-2)%mod;//计算在num-1个数中取k-1个的组合数
}

int32_t main()
{
    
    
    IOS;
    cas[0]=cas[1]=1;
    for(ll i=2;i<maxn;i++) cas[i]=cas[i-1]*i%mod;
    cin>>n>>k;
    n<<=1;
    for(ll i=0;i<n;i++)
    {
    
    
        ll x;
        cin>>x;
        node.push_back({
    
    x,(i&1)?-1:1});
    }
    sort(node.begin(),node.end(),cmp);
    ll ans=0,num=0;
    for(ll i=0;i<n;i++)
    {
    
    
        if(node[i].ope==1)
        {
    
    
            num++;
            ans=(ans+cal(num))%mod;
        }
        else num--;
    }
    cout<<ans<<endl;
}

Supongo que te gusta

Origin blog.csdn.net/StandNotAlone/article/details/108862525
Recomendado
Clasificación