zoukankan      html  css  js  c++  java
  • ZROI#956

    ZROI#956

    ZROI#956
    这题难死我了...
    不过这题的(55pts)暴力被我校大佬们当成了(set)练习题,(100pts)是一个很妙的(01trie)做法.
    (Subtask1)就模拟就行了,太水了不讲.
    先讲一讲(Subtask3)吧,用一个(multiset)维护,不过要维护一个全局标记(tag),表示当前加过的数字的总和.
    每次插入一个新数字(x)就插入((x-tag+mod)%mod),这是因为我们的每次查询/删除都要查询/删除((x-tag+mod)%mod).
    一定要记得((+mod)%mod),笔者就是因为这个原因删除不存在的元素导致疯狂(RE).

    再说一说(Subtask2)吧,这个同理,也是维护一个全局懒标记(tag),不过每次插入(x)的时候变成了插入(x :xor :tag),这是因为(xor)的逆操作是(xor).
    查询和删除同理.

    结合这两种算法和模拟总共可以得到(55pts)的分数,可谓十分不错.
    (Code:)

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <queue>
    #include <cmath>
    #include <ctime>
    #include <map>
    #include <set>
    #define MEM(x,y) memset ( x , y , sizeof ( x ) )
    #define rep(i,a,b) for (int i = a ; i <= b ; ++ i)
    #define per(i,a,b) for (int i = a ; i >= b ; -- i)
    #define pii pair < int , int >
    #define X first
    #define Y second
    #define rint read<int>
    #define int long long
    #define pb push_back
    
    using std::set ;
    using std::pair ;
    using std::max ;
    using std::min ;
    using std::priority_queue ;
    using std::vector ;
    using std::multiset ;
    
    template < class T >
        inline T read () {
            T x = 0 , f = 1 ; char ch = getchar () ;
            while ( ch < '0' || ch > '9' ) {
                if ( ch == '-' ) f = - 1 ;
                ch = getchar () ;
            }
            while ( ch >= '0' && ch <= '9' ) {
                x = ( x << 3 ) + ( x << 1 ) + ( ch - 48 ) ;
                ch = getchar () ;
           }
       return f * x ;
    }
    
    const int N = 5e5 + 100 ;
    
    vector < int > v ;
    multiset < int > s ; int t[N] , siz , types[5] ;
    int n , q , x , opt , mod = pow ( 2 , 30 ) , tag ;
    
    struct operators { int val , type ; } op[N] ;
    
    inline void solve1 () {
        while ( q -- ) {
            opt = rint () ;
            if ( opt == 1 ) t[++siz] = rint () ;
            if ( opt == 2 ) {
                int pos ; x = rint () ;
                rep ( i , 1 , siz ) if ( t[i] == x ) { pos = i ; break ; }
                -- siz ; rep ( i , pos , siz ) t[i] = t[i+1] ;
            }
            if ( opt == 3 ) rep ( i , 1 , siz ) t[i] = ( t[i] + 1 ) % mod ;
            if ( opt == 4 ) { x = rint () ; rep ( i , 1 , siz ) t[i] ^= x ; }
        }
        std::sort ( t + 1 , t + siz + 1 ) ;
        rep ( i , 1 , siz ) printf ("%lld " , t[i] ) ;
    }
    
    inline void solve2 () {
        rep ( i , 1 , q ) {
            opt = op[i].type ;
            if ( opt == 1 ) s.insert ( op[i].val ^ tag ) ;
            if ( opt == 2 ) s.erase ( s.find ( op[i].val ^ tag ) ) ;
            if ( opt == 4 ) tag ^= op[i].val ;
        }
        for ( auto k : s ) v.pb ( k ^ tag ) ; std::sort ( v.begin () , v.end () ) ;
        for ( int k : v ) printf ("%lld " , k ) ;
        return ;
    }
    
    inline void solve3 () {
        rep ( i , 1 , q ) {
            opt = op[i].type ;
            if ( opt == 1 ) s.insert ( ( op[i].val - tag + mod ) % mod ) ;
            if ( opt == 2 ) s.erase ( s.find ( ( op[i].val - tag + mod ) % mod ) ) ;
            if ( opt == 3 ) ++ tag ;
        }
        for ( auto k : s ) v.pb ( ( k % mod + tag ) % mod ) ;
        std::sort ( v.begin () , v.end () ) ; for ( int k : v ) printf ("%lld " , k ) ;
    }
    
    signed main (int argc , char * argv[] ) {
        n = rint () ; q = rint () ;
        rep ( i , 1 , n ) { x = rint () ; s.insert ( x ) ; t[++siz] = x ; }
        if ( n <= 5e3 && q <= 5e3 ) solve1 () ; 
        else {
            rep ( i , 1 , q ) { op[i].type = rint () ; if ( op[i].type != 3 ) op[i].val = rint () ; ++ types[op[i].type] ; }
            if ( ! types[3] ) solve2 () ; else if ( ! types[4] ) solve3 () ;
        }
        return 0 ;
    }
    

    接下来是正解做法:
    还是用全局懒标记维护异或操作.
    但是(+1)之后再(mod: 2^{30})这个操作怎么处理呢?
    你考虑,这个操作相当于是在二进制形式中寻找一个(i),使得(a_1,a_2...a_{i-1})都是(1),而(a_i)(0),然后翻转(a_1,a_2,a_3...a_{i-1},a_{i})即可.
    这个问题,我们可以放到(01Trie)上去做,不过你可能会发现这个东西需要你从叶子去找一段连续的(1),这就很难办.
    所以我们选择,从低位到高位建树,这样就是自然地从根节点找一段连续的(1)了.
    加上异或操作之后,记得统计全局标记就行了.不过加上异或之后我们要找的就不一定是一段连续的(1),为了使异或对应关系一致,所以需要异或一个(maxsize-1)去找.
    (Code:)

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <queue>
    #include <cmath>
    #include <ctime>
    #include <map>
    #include <set>
    #define MEM(x,y) memset ( x , y , sizeof ( x ) )
    #define rep(i,a,b) for (int i = a ; i <= b ; ++ i)
    #define per(i,a,b) for (int i = a ; i >= b ; -- i)
    #define pii pair < int , int >
    #define X first
    #define Y second
    #define rint read<int>
    #define int long long
    #define pb push_back
    
    using std::set ;
    using std::pair ;
    using std::max ;
    using std::min ;
    using std::priority_queue ;
    using std::vector ;
    using std::swap ;
    using std::sort ;
    using std::unique ;
    using std::greater ;
    
    template < class T >
        inline T read () {
            T x = 0 , f = 1 ; char ch = getchar () ;
            while ( ch < '0' || ch > '9' ) {
                if ( ch == '-' ) f = - 1 ;
                ch = getchar () ;
            }
            while ( ch >= '0' && ch <= '9' ) {
                x = ( x << 3 ) + ( x << 1 ) + ( ch - 48 ) ;
                ch = getchar () ;
           }
       return f * x ;
    }
    
    const int N = 3e5 + 100 ;
    const int maxsize = ( 1 << 30 ) - 1 ;
    
    vector < int > ans ;
    int n , opt , u , v , q[33] , cnt , m ;
    int ch[N<<5][2] , sum[(N<<5)] , tag ;
    
    inline void insert (int cur , int val) {
        int now = 0 ;
        for (int i = 0 ; i < 30 ; ++ i) {
            int son = ( cur >> i ) & 1 ;
            if ( ! ch[now][son] ) ch[now][son] = ++ cnt ;
            now = ch[now][son] ;
        }
        sum[now] += val ; return ;
    }
    
    inline void reverse () {
        int now = 0 , siz = 0 , cur = maxsize ^ tag ;
        for (int i = 0 ; i < 30 ; ++ i) {
            int son = ( cur >> i ) & 1 ;
            q[++siz] = now ;
            if ( ! ch[now][son] ) break ;
            now = ch[now][son] ;
        }
        rep ( i , 1 , siz ) swap ( ch[q[i]][0] , ch[q[i]][1] ) ;
        return ;
    }
    
    inline void dfs (int cur , int dep , int val) {
        if ( dep >= 30 ) {
            rep ( i , 1 , sum[cur] ) ans.pb ( val ^ tag ) ;
            return ;
        }
        if ( ch[cur][0] ) dfs ( ch[cur][0] , dep + 1 , val ) ;
        if ( ch[cur][1] ) dfs ( ch[cur][1] , dep + 1 , val | ( 1 << dep ) ) ;
        return ;
    }
    
    signed main (int argc , char * argv[] ) {
        n = rint () ; m = rint () ; rep ( i , 1 , n ) insert ( rint () , 1 ) ;
        while ( m -- ) {
            int opt = rint () ;
            if ( opt == 1 ) insert ( rint () ^ tag , 1 ) ;
            if ( opt == 2 ) insert ( rint () ^ tag , - 1 ) ;
            if ( opt == 3 ) reverse () ;
            if ( opt == 4 ) tag ^= rint () ;
        }
        dfs ( 0 , 0 , 0 ) ; sort ( ans.begin () , ans.end () ) ;
        for (int k : ans ) printf ("%lld " , k ) ;
        system ("pause") ; return 0 ;
    }
    
    May you return with a young heart after years of fighting.
  • 相关阅读:
    Git和SourceTree配合使用
    hive中数据存储格式对比:textfile,parquent,orc,thrift,avro,protubuf
    hive 实现类似 contain 包含查询
    hive函数 parse_url的使用
    Spring中注解方式的默认beanName生成规则
    @Autowired 与@Resource的区别
    js通过html的url获取参数值
    mysql模糊查询多个字段
    java获取调用当前方法的方法名和行数
    阿里druid数据源配置及数据库密码加密
  • 原文地址:https://www.cnblogs.com/Equinox-Flower/p/11459834.html
Copyright © 2011-2022 走看看