Luogu题目选做(三)

P2249

二分答案

直接套用lower_bound()

#include <bits/stdc++.h>
using namespace std;


const int N = 1e6 + 5;
int n , m , a[N];


inline int read()
{
    register int x = 0;

    register char ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
    return x;
}

int main()
{
    n = read() , m = read();
    for( register int i = 1 ; i <= n ; i ++ ) a[i] = read();
    for( register int i = 1 , x , pos ; i <= m ; i ++ )
    {
        x = read() , pos = lower_bound( a + 1 , a + 1 + n , x ) - a;
        printf( "%d " , pos > n || a[pos] > x ? -1 : pos );
    }
    return 0;
}

Hash

可以直接用Hash储存每个数第一次出现的的位置

#include<bits/stdc++.h>
using namespace std;


const int Mod = 1e5+7;
int n , m ;
vector< pair<int , int > > hash_map[Mod];


inline int read()
{
    register int x = 0;
    register char ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
    return x;
}


inline int check( int x , int key )
{
    for( auto it : hash_map[key] ) if( x == it.first ) return it.second;
    return -1;
}


int main()
{
    n = read() , m = read();
    for( register int i = 1 , x , key , f ; i <= n ; i ++ )
    {
        x = read() , key = x % Mod , f = 0;
        if( check( x , key ) == -1 ) hash_map[key].emplace_back( x , i );
    }
    for( register int i = 1 , x ; i <= m ; i ++ ) x = read() , printf("%d " , check( x , x % Mod ) );
    return 0;
}

P2068

树状数组模板题

#include <bits/stdc++.h>
using namespace std;


#define ll long long
#define lowbit( x ) ( x & -x )


const int N = 1e5 + 5;
int n , m ;
ll bit[N];


inline int read()
{
    int x = 0;
    char ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
    return x;
}

inline bool opt()
{
    char ch = getchar();
    while( ch != 'x' && ch != 'y' ) ch = getchar();
    return ( ch == 'x' ? 1 : 0 );
}

inline void update( int x , int y )
{
    for( int i = x ; i <= n ; i += lowbit( i ) ) bit[i] += y;
}

inline ll get( int x )
{
    ll sum = 0;
    for( int i = x ; i ; i -= lowbit( i ) ) sum += bit[i];
    return sum;
}


int main()
{
    n = read() , m = read();
    for( int i = 1 , x , y ; i <= m ; i ++ )
    {
        if( opt() ) x = read() , y = read() , update( x , y );
        else x = read() , y = read() , printf( "%lld\n" , get(y) - get( x - 1 ) );
    }
    return 0;
}

P1637

跑两边树状数组

先正着跑一边算出lower[i],代表1i-1中小于a[i]的数的个数

再倒着跑一遍算出upper[i],代表i+1n中大于a[i]的数的个数

枚举三元中间的一个,1i-1任何一个小于a[i]的都可以做第一个元,i+1n中任何一个大于a[i]的都可以做第三个元

所以答案\(ans = \sum_{i=1}^n lower_i \times upper_i\)

对于a[i]尽管范围很大但总数很小哈希一下就好

#include <bits/stdc++.h>
using namespace std;


#define ll long long
#define lowbit( x ) ( x & -x )
#define val first
#define id  second

const int N = 3e4 + 5;
int n , m , bit[N] , lower[N] , upper[N];
ll a[N] , b[N] , ans;

inline ll read()
{
    ll x = 0;
    char ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
    return x;
}

inline void update( int x , int y )
{
    for( int i = x ; i <= m ; i += lowbit( i ) ) bit[i] += y;
}

inline int get( int x )
{
    int sum = 0;
    for( int i = x ; i ; i -= lowbit( i ) ) sum += bit[i];
    return sum;
}


int main()
{
    n = read();
    for( int i = 1 ; i <= n ; i ++ ) a[i] = b[i] = read();
    sort( b + 1 , b + 1 + n );
    m = unique( b + 1  , b + 1 + n ) - b - 1;
    for( int i = 1 ; i <= n ; i ++ ) a[i] = lower_bound( b + 1 , b + 1 + m , a[i] ) - b;

    for( int i = 1 ; i <= n ; i ++ )
    {
        lower[i] = get( a[i] - 1 );
        update( a[i] , 1 );
    }

    memset( bit , 0 , sizeof( bit ) );
    for( int i = n ; i >= 1 ; i -- )
    {
        update( a[i] , 1 );
        upper[i] = n - i + 1 - get( a[i] );
    }

    for( int i = 1 ; i <= n ; i ++ ) ans += lower[i] * upper[i];
    cout << ans << endl;
}

P1276

线段树

可以用线段树来维护所有状态相同的点来降低复杂度,在修改的操作时顺便将一些状态相同的点给合并。

对于线段树的每个节点有四种状态

  • 1 该区间全部是树木
  • 2 该区间全部是树苗
  • 0 该区间全部为坑
  • -1 该区间内部点状态不同

对于查询,没有必要用线段树进行维护,我们只需要在修改的时候统计结果即可

在修改时,如果该区间为-1则递归操作两个子节点,并在操作后考虑吧时候能够合并两个子节点为一个节点

至于从0开始,将所有的点偏移一个单位即可

#include <bits/stdc++.h>
using namespace std;


const int N = 1e4 + 5;
int n , m , ans1 , ans2 ;// ans1 现有多少树苗 , ans2 有多少树苗被砍掉

struct Node
{
    int l , r , value;// 0 没有树 1树木 2 树苗 -1不能当作一个整体
    Node * left , * right;
    Node( int l , int r , int value , Node * left , Node * right ) : l(l) , r(r) , value(value) , left(left) , right(right){};
} * root;


inline int read()
{
    register int x = 0 , f = 1 , ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
    return x;
}

inline Node * build( int l , int r )
{
    if( l == r ) return new Node( l , r , 1 , 0 , 0 );
    register int mid = ( l + r ) >> 1;
    Node * left = build( l , mid ) , * right = build( mid + 1 , r );
    return new Node( l , r , 1 , left , right );
}

inline void modify_cut( int l , int r , Node * cur )
{
    register int mid = ( cur -> l + cur -> r ) >> 1;
    if( cur -> value != -1 && cur -> value != 1 && cur -> left ) cur -> left -> value = cur -> value ,  cur -> right -> value = cur -> value;
    if( l <= cur -> l && r >= cur -> r )
    {
        if( cur -> value == 0 ) return ;
        if( cur -> value == 1 ) cur -> value = 0;
        if( cur -> value == 2 ) cur -> value = 0 , ans1 -= cur -> r - cur -> l + 1 , ans2 += cur -> r - cur -> l + 1;
        if( cur -> value == -1 ) cur -> value = 0 , modify_cut( l , r , cur -> left ) , modify_cut( l , r , cur -> right );
    }
    else
    {
        if( l <= mid ) modify_cut( l , r , cur -> left );
        if( r > mid ) modify_cut( l , r , cur -> right );
        if( cur -> left -> value != cur -> right -> value ) cur -> value = -1;
        else cur -> value = cur -> left -> value;
    }
}

inline void modify_plant( int l , int r , Node * cur )
{
    if( cur -> value != -1 && cur -> value != 1 && cur -> left) cur -> left -> value = cur -> right -> value = cur -> value;
    if( l <= cur -> l && r >= cur -> r )
    {
        if( cur -> value >= 1 ) return ;
        if( cur -> value == 0 ) cur -> value = 2 , ans1 += cur -> r - cur -> l + 1;
        if( cur -> value == -1 ) modify_plant( l , r , cur -> left ) , modify_plant( l , r , cur -> right );
    }
    else
    {
        register int mid = ( cur -> l + cur -> r ) >> 1;
        if( l <= mid ) modify_plant( l , r ,  cur -> left );
        if( r > mid ) modify_plant( l , r , cur -> right );
        if( cur -> left -> value != cur -> right -> value ) cur -> value = -1;
        else cur -> value = cur -> left -> value;
    }
}


int main()
{
    n = read() + 1 , m = read();
    root = build( 1 , n );
    for( register int i = 1 , op , l , r ; i <= m ; i ++ )
    {
        op = read() , l = read() + 1 , r = read() + 1;
        if( l > r ) swap( l , r );
        if( op == 0 ) modify_cut( l , r , root );
        else modify_plant( l , r , root );
    }
    cout << ans1 << endl << ans2 << endl;
    return 0;
}

暴力

写完后计算复杂度发现数据太水,暴力模拟都能过,复杂度应该是\(O(n\times L)\)

#include <bits/stdc++.h>

using namespace std;

const int N = 1e4 + 5;
int n , m , ans1 , ans2 , a[N];

inline int read()
{
    register int x = 0 , ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
    return x;
}

int main()
{
    n = read() + 1 , m = read();
    memset( a , 1 , sizeof(a) );
    for( register int i = 1 , op , l , r ; i <= m ; i ++ )
    {
        op = read() , l = read() + 1 , r = read() + 1;
        if( op == 0 )
        {
            for( register int j = l ; j <= r ; j ++ )
            {
                if( !a[j] ) continue;
                if( a[j] == 2 ) ans1 -- , ans2 ++;
                a[j] = 0;
            }
        }
        else
        {
            for( register int j = l ; j <= r ; j ++ )
            {
                if( a[j] ) continue;
                a[j] = 2 , ans1 ++;
            }
        }
    }
    cout << ans1 << endl << ans2 << endl;
    return 0;
}
posted @ 2021-11-19 14:11  PHarr  阅读(28)  评论(0编辑  收藏  举报