• Manthan, Codefest 19 题解


    Manthan, Codefest 19

    A XORinacci

    显然循环节是3

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define MAXN 200006
    int n , m , t;
    int A[MAXN];
    int main() {
        int T;cin >> T;
        while( T-- ) {
            cin >> n >> m >> t;
            if( t % 3 == 0 ) cout << n << endl;
            else if( t % 3 == 1 ) cout << m << endl;
            else cout << (n ^ m) << endl;
        }
    
    }
    
    

    B Uniqueness

    又FST了。。。。。

    明明显然的单log为了追求速度写了个双log成功fst

    实际上是可以二分+check的,开头离散化一下就好了。

    其实(n^2log)很好想,只是懒得改了2333

    #pragma GCC optimize(3)
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<set>
    using namespace std;
    #define MAXN 200006
    int n , m , t;
    int A[MAXN];
    int a[MAXN] , M[MAXN];
    bool chk( int len ) {
        for( int i = 1 ; i <= n - len + 1 ; ++ i ) {
            int flg = 0;
            memset( M , 0 , sizeof M );
            for( int j = 1 ; j <= n ; ++ j ) {
                if( j >= i && j <= i + len - 1 ) continue;
                if( M[a[j]] ) {flg = 1;break;}
                M[a[j]] = 1;
            }
            if( !flg ) return true;
        }
        return false;
    }
    int main() {
        cin >> n;
        for( int i = 1 ; i <= n ; ++ i ) scanf("%d",&A[i]) , a[i] = A[i];
        sort( A + 1 , A + 1 + n );
        int sz = unique( A + 1 , A + 1 + n ) - A - 1;
        for( int i = 1 ; i <= n ; ++ i ) a[i] = lower_bound( A + 1 , A + 1 + sz , a[i] ) - A;
        int l = 0 , r = n;
        while( l <= r ) {
            int mid = l + r >> 1;
            if( chk( mid ) ) r = mid - 1;
            else l = mid + 1;
        }
        cout << l << endl;
    }
    

    C Magic Grid

    先构造 $ 4 imes 4 $ 矩阵

    然后直接把这个矩阵+16 , +32 ... 复制 $ frac{n}{4} imes frac{n}{4} $ 遍就好了

    因为每个小矩阵都是0,所以肯定大矩阵也是0

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define int long long
    #define MAXN 1006
    int n , data[MAXN][MAXN] , M[6][6];
    signed main() {
        cin >> n;
            int x = 15, tot = 0;
        int now = 0;
        for (int i = 1; i <= 4; ++i)
            for (int j = 1; j <= 4; ++j)
                M[i][j] = x - now, now++;
        n /= 4;
        int cur = 0;
        for( int i = 0 ; i < n ; ++ i )
            for( int j = 0 ; j < n ; ++ j ) {
                for( int k = 1 ; k <= 4 ; ++ k )
                    for( int kk = 1 ; kk <= 4 ; ++ kk )
                        data[i*4+k][j*4+kk] = M[k][kk] + cur * 16;
                ++ cur;
            }
        for( int i = 1 ; i <= n * 4 ; ++ i ) {
            for (int j = 1; j <= n * 4; ++j)
                printf("%lld ", data[i][j]);
            puts("");
        }
    }
    

    D Restore Permutation

    树状数组+二分

    显然最后一个位置可以决定最后一位是多少,然后就变成了个几乎一样的问题,从后往前枚举然后二分一下就好了。

    然而题解做法是找到1的位置,然后给后面的位置+1,用了个线段树,虽然是单log优秀一些但是。。不好写啊2333

    毕竟cf 2e5显然是随便跑的了

    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<map>
    using namespace std;
    #define MAXN 200006
    #define int long long
    int n;
    
    int T[MAXN];
    void add( int x , int c ) {
        while( x <= n ) T[x] += c , x += x & -x;
    }
    int que( int x ) {
        int ret = 0;
        while( x > 0 ) ret += T[x] , x -= x & -x;
        return ret;
    }
    int S[MAXN];
    int ans[MAXN];
    signed main() {
        cin >> n;
        for( int i = 1 ; i <= n ; ++ i ) scanf("%lld",&S[i]);
        for( int i = 1 ; i <= n ; ++ i ) add( i , i );
        for( int i = n ; i >= 1 ; -- i ) {
            int l = 0 , r = n;
            while( l <= r ) {
                int mid = l + r >> 1;
                if( que( mid ) > S[i] ) r = mid - 1;
                else l = mid + 1;
            }
            add( l , -l );
            ans[i] = l;
    
        }
        for( int i = 1 ; i <= n ; ++ i ) printf("%lld ",ans[i]);
    }
    
    

    E Let Them Slide

    首先枚举位置 1n

    然后我们对每个数组维护一个 LR 表示这个数组在当前位置上可以滑到的区间

    显然每个位置的移动次数和是只有 (sum a) ,可以接受,随便拿个数据结构维护一下区间最大就好了

    #include <cstdio>
    #include <cstring>
    #include <set>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    int n, w;
    #define MAXN 1000006
    typedef long long ll;
    int read(  ) {
        char ch = ' '; int res = 0;
        while( ch < '0' || ch > '9' ) ch = getchar();
        while( ch <= '9' && ch >= '0' ) { res *= 10 , res += ch - '0' , ch = getchar(); }
        return res;
    }
    
    ll T[MAXN << 2];
    void mdfy(int rt,int l,int r,int L,int R,int valx) {
        if(L <= l && r <= R) { T[rt] += valx; return ; }
        int m = (l + r) >> 1;
        if(m >= L) mdfy( rt << 1 , l , m , L , R , valx );
        if(m <= R - 1) mdfy( rt << 1 | 1 , m + 1 , r , L , R , valx);
    }
    void work(int rt,int l,int r) {
        if (l == r) { printf("%lld ", T[rt]); return; }
        T[rt << 1] += T[rt] , T[rt << 1 | 1] += T[rt];
        int mid = (l + r) >> 1;
        work(rt << 1, l, mid) , work(rt << 1 | 1, mid + 1, r);
    }
    
    struct node {
        ll val;
        int pos;
        node(  ) { val = pos = 0; }
    }A[1000500];
    
    bool cmp( node a , node b ) {
        return a.val > b.val;
    }
    
    set<int> st;
    int main() {
        n = read() , w = read();
        for (int i = 1 , l; i <= n; ++i) {
            st.clear();
            l = read();
            for (int j = 1; j <= l; ++j) A[j].pos = j, scanf("%lld", &A[j].val);
            int que = l , len = w - l + 1;
            sort(A + 1, A + 1 + l , cmp);
            for (int j = 1; j <= l; ++j) {
                int l = A[j].pos, r = A[j].pos + len - 1;
                auto it = st.lower_bound(A[j].pos), it1 = it;
                if (A[j].val < 0) r = min(r, que) , l = max(l, w - que + 1);
                if (it != st.end()) r = min(r, (*(it)) - 1);
                if (it1 != st.begin()) l = max(l, (*(--it1)) + len);
                if (l <= r) mdfy(1, 1, w, l, r, A[j].val);
                st.insert(A[j].pos);
            }
        }
        work(1, 1, w);
    }
    
    

    F Bits And Pieces

    这个题很有意思

    我们考虑维护一个数据结构(其实就是暴力),支持插入,查询 某个数字 作为 子集 的数 可不可以被当前集合内部的数通过 操作得到

    由于只需要查询是否可以被与得到,我们知道,如果一个数字 $ a $ 是两个集合内的数字的子集,那么它显然可以通过与得到一个数使得 $ a $ 是它的子集。

    那么考虑对一个数 用dfs 的方法来枚举子集。如果这个数的某个子集已经出现了两次,就不用继续枚举这个数字的子集了,因为它的所有子集必定已经被以前枚举过两次了。

    所以总复杂度是 $ 2 v $ 的!

    那么放到这个题,从后往前插入,然后对每个数字考虑它来或,先把当前决定从与集合中拿出的数字置0,或的时候可以从高位到低位看看这位是否有1,如果没有就找找当前值把这位变1能不能通过两个数字与得到,如果可以就把当前值这一位变成1。

    具体实现可以看代码,很nb的一个思路。。

    (然而我根本不知道啥是题解说的sosdp)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define MAXN 2097156
    
    int read(  ) {
        int res = 0; char ch = ' ';
        while( ch > '9' || ch < '0' ) ch = getchar();
        while( ch >= '0' && ch <= '9' ) res *= 10 , res += ch - '0' , ch = getchar();
        return res;
    }
    
    int cnt[MAXN];
    void insert( int x , int y ) {
        if( cnt[x | y] >= 2 ) return;
        if( x == 0 ) { ++ cnt[y]; return; }
        insert( x & x - 1 , y | ( x & -x ) ) ,
        insert( x & x - 1 , y );
    }
    int n , ans = 0;
    int A[MAXN];
    int main() {
        cin >> n;
        for( int i = 1 ; i <= n ; ++ i ) A[i] = read();
        for( int i = n , cur ; i >= 1 ; -- i ) {
            if( i <= n - 2 ) {
                cur = 0;
                for( int j = 21 ; j >= 0 ; -- j ) if( ( ~ A[i] >> j & 1 ) && cnt[ cur | ( 1 << j ) ] == 2 )
                        cur |= ( 1 << j );
                ans = max( ans , A[i] | cur );
            }
            insert( A[i] , 0 );
        }
        cout << ans << endl;
    }
    
    

    G Polygons

    首先,我们拿的图形完全可以拥有同一个顶点(感性理解)

    考虑拿了 k 边形,那么肯定会拿 k 的约数边形状,毕竟点的个数不增加嘛~

    考虑我们当前想要拿 x 边形,在此之前我们显然已经把 x 的约数边形拿完了(不然为啥不拿约数边形呢?),此时点的个数的增加量明显是 (phi(x)) ,因为可以把每个点看成是 $ 1/x , 2/x , 3/x ... $,其中不互质的在约数时候已经拿过了呢。

    明显,二边形和一边形是不存在的,

    • 一边形 明显,直接答案+1就好了
    • 二边形 当我们选择了一个偶数边形,就意味着我们选择了二边形。也就是说,除非只选正三角形,都是会把二边形选上的。只选择正三角形的情况只有一种,只需要特判 $ k = 1 $ 即可。

    综上所述,当 $ k = 1 $ 可以直接输出 3 , 其他时候对 $ phi $ 排序,然后从小到大拿 $ k + 2 $ 个就好了。

    由于一个显然的结论, $ phi(d) leq phi( x ) $ 其中 $ d $ 是 $ x $ 的约数,我们一定会在选择 $ x $ 边形前选择完所有的 $ x $ 约数边形。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define MAXN 1000006
    #define int long long
    int n , k;
    
    int p[MAXN] , cnt , phi[MAXN];
    void init( ) {
        phi[1] = 1;
        for( int i = 2 ; i < MAXN ; ++ i ) {
            if( !p[i] ) p[++cnt] = i , phi[i] = i - 1;
            for( int j = 1 ; j <= cnt && p[j] * i < MAXN ; ++ j ) {
                p[p[j] * i] = 1;
                if( i % p[j] == 0 ) { phi[ p[j] * i ] = phi[i] * ( p[j] ); break; }
                phi[p[j] * i] = phi[i] * ( p[j] - 1 );
            }
        }
    }
    
    signed main() {
        init();
        cin >> n >> k;
        if( k == 1 ) return puts( "3" ) , 0;
        sort( phi + 1 , phi + 1 + n );
        int res = 0;
        for( int i = 1 ; i <= k + 2 ; ++ i ) res += phi[i];
        cout << res << endl;
    }
    
    

    H Red Blue Tree

    它没有鸽

  • 相关阅读:
    redis
    docker :no such file or directory
    删除Linux的用户
    lunux系统安全
    centos7.4yum错误
    POI2014 HOT-Hotels
    POI2009 KON-Ticket Inspector
    CF140E New Year Garland
    CF392B Tower of Hanoi
    落谷 P2401 不等数列
  • 原文地址:https://www.cnblogs.com/yijan/p/cf1208.html
Copyright © 2020-2023  润新知