zoukankan      html  css  js  c++  java
  • 【8.24校内测试】【二分+贪心】【线段树模拟】【整除分类】

    把所有人的位置和钥匙的位置排序,可以发现,最优的选择钥匙的方法一定是选择一段连续的区间,暴力枚举即可。当然,二分最优时间贪心去判断也是可以的,复杂度相对枚举更优。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cmath>
    #include<algorithm>
    #define ll long long
    using namespace std;
    
    int n, k;
    ll p;
    ll a[5005], b[10005];
    
    bool check ( int mid ) {
        int i = 1, j = 1;
        while ( j <= k ) {
            if ( (ll)abs ( b[j] - a[i] ) + abs ( p - b[j] ) <= mid )
                i ++;
            if ( i == n + 1 ) break;
            j ++;
        }
        return i == n + 1;
    }
    
    ll erfen ( ) {
        ll l = 0, r = 1e9, ans;
        while ( l <= r ) {
            int mid = ( l + r ) >> 1;
            if ( check ( mid ) ) {
                ans = mid; r = mid - 1;
            } else l = mid + 1;
        }
        return ans;
    }
    
    int main ( ) {
        //freopen ( "keys.in", "r", stdin );
        //freopen ( "keys.out", "w", stdout );
        scanf ( "%d%d%I64d", &n, &k, &p );
        for ( int i = 1; i <= n; i ++ )    scanf ( "%I64d", &a[i] );
        for ( int i = 1; i <= k; i ++ )    scanf ( "%I64d", &b[i] );
        sort ( a + 1, a + 1 + n );
        sort ( b + 1, b + 1 + k );
        ll ans = erfen ( );
        printf ( "%I64d", ans );
        return 0;
    }

    每次我们只需要找到下一个最小值,计算这段区间还剩下的牌的张数,再删除这个最小值即可。一道小模拟,考虑用数据结构维护。

    原序列建成一棵线段树,结构体中维护区间和,区间最小值,以及最小值所在位置。区间和是牌张数的和,每次删除一个点,把这个点的最小值改成正无穷,就可以更新了。

    查询时需要查询$1-pos$和$pos-n$两段区间,$pos$指现在上一次删除的点位置,判断两个区间最小值大小,优先删除后面的区间。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define ll long long
    using namespace std;
    
    int n;
    int las[100005];
    
    struct tree {
        ll sum;
        int mi, pos;
    } TR[100005*4];
    
    struct node {
        int v, id;
    } a[100005];
    bool cmp ( node a, node b ) {
        if ( a.v == b.v ) return a.id < b.id;
        return a.v < b.v;
    }
    
    void update ( int nd ) {
        TR[nd].sum = TR[nd << 1].sum + TR[nd << 1 | 1].sum;
        if ( TR[nd << 1 | 1].mi < TR[nd << 1].mi ) {
            TR[nd].mi = TR[nd << 1 | 1].mi;
            TR[nd].pos = TR[nd << 1 | 1].pos;
        } else if ( TR[nd << 1].mi < TR[nd << 1].mi ){
            TR[nd].mi = TR[nd << 1].mi;
            TR[nd].pos = TR[nd << 1].pos;
        } else {
            TR[nd].mi = TR[nd << 1].mi;
            TR[nd].pos = min ( TR[nd << 1].pos, TR[nd << 1 | 1].pos );
        }
    }
    
    void build ( int nd, int l, int r ) {
        if ( l == r ) {
            TR[nd].sum = 1;
            TR[nd].mi = a[l].v;
            TR[nd].pos = a[l].id;
            return ;
        }
        int mid = ( l + r ) >> 1;
        build ( nd << 1, l, mid );
        build ( nd << 1 | 1, mid + 1, r );
        update ( nd );
    }
    
    void add ( int nd, int l, int r, int pos, int d ) {
        if ( l == r ) {
            TR[nd].sum += d;
            if ( !TR[nd].sum ) TR[nd].mi = 0x3f3f3f3f;
            return ;
        }
        int mid = ( l + r ) >> 1;
        if ( pos <= mid ) add ( nd << 1, l, mid, pos, d );
        else add ( nd << 1 | 1, mid + 1, r, pos, d );
        update ( nd );
    }
    
    tree query ( int nd, int l, int r, int L, int R ) {
        if ( l >= L && r <= R ) return TR[nd];
        tree ans; ans.mi = 0x3f3f3f3f; ans.sum = 0; ans.pos = 0; 
        int mid = ( l + r ) >> 1;
        if ( L <= mid ) {
            tree tmp = query ( nd << 1, l, mid, L, R );
            ans.sum += tmp.sum;
            if ( ans.mi > tmp.mi ) {
                ans.mi = tmp.mi;
                ans.pos = tmp.pos;
            } else if ( ans.mi == tmp.mi ) {
                ans.pos = min ( ans.pos, tmp.pos ); 
            }
        }
        if ( R > mid ) {
            tree tmp = query ( nd << 1 | 1, mid + 1, r, L, R );
            ans.sum += tmp.sum;
            if ( ans.mi > tmp.mi ) {
                ans.mi = tmp.mi;
                ans.pos = tmp.pos;
            } else if ( ans.mi == tmp.mi ) {
                ans.pos = min ( ans.pos, tmp.pos ); 
            }
        }
        return ans;
    }
    
    int main ( ) {
        freopen ( "cards.in", "r", stdin );
        freopen ( "cards.out", "w", stdout );
        scanf ( "%d", &n );
        for ( int i = 1; i <= n; i ++ )    {
            scanf ( "%d", &a[i].v ), a[i].id = i;
        }
        build ( 1, 1, n );
        sort ( a + 1, a + 1 + n, cmp );
        int pre = 1;
        ll ans = 0;
        for ( int i = 1; i <= n; i ++ ) {
            tree q1 = query ( 1, 1, n, pre, n );
            tree q2 = query ( 1, 1, n, 1, pre );
            int pos;
            if ( q1.mi <= q2.mi ) {
                pos = q1.pos;
                ans += query ( 1, 1, n, pre, pos ).sum;
            } else {
                pos = q2.pos;
                ans += q1.sum; 
                ans += query ( 1, 1, n, 1, pos ).sum;
            }
            add ( 1, 1, n, pos, -1 );
            pre = pos;
        }
        printf ( "%I64d", ans );
        return 0;
    }

    转换题意,我们要求的实际上是使$sum {left lceil frac{a_i}{d} ight ceil*d-a_i}<=k$成立的最大$d$,可以发现,对于同一个$a_i$来说,$left lceil frac{a_i}{d} ight ceil$的取值有$sqrt{a_i}$个,$n$个$a_i$就有$nsqrt{a_i}$个,我们可以把所有使$left lceil frac{a_i}{d} ight ceil$相同的$d$分成一组,处理下来,每一组$d$中的$left lceil frac{a_i}{d} ight ceil$都相同,此时这组$d$算出来的答案就具有二分性了。

    接下来是如何分组的问题。我们设$x=left lceil frac{a_i}{d} ight ceil$,那么$x-1<frac{a_i}{d}<=x$,所以$frac{a_i}{x}<=d$,意味着,对于每一段连续相同的$x$,$d$的范围是可以确定的,因为是$>=$,我们就可以倒着处理$d$,每次算出一个范围的起点$d$,那么$leftlceilfrac{a}{d-1} ight ceil$就是下一段的$x'$,$left lceil frac{a}{x'} ight ceil$就是下一个范围的起点$d'$了。我们把每个起点存到结构体,排序加二分就行了。注意离散化。

    可是$stl$好慢aaa!!怎么搞都t!!

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    #define ll long long
    using namespace std;
    
    int n;
    vector < ll > vc;
    ll k, a[105];
    
    void split ( ll a, vector < ll > &vc ) {
        for ( ll d = a; d; ) {
            ll x = ( a + d - 1 ) / d;
            ll k = ( a + x - 1 ) / x;
            vc.push_back ( k );
            d = k - 1;
        }
    }
    
    bool check ( ll mid ) {
        ll ans = 0;
        for ( int i = 1; i <= n; i ++ ) {
            ans += ( a[i] + mid - 1 ) / mid * mid - a[i];
        }
        return ans <= k;
    }
    
    int main ( ) {
        freopen ( "bamboo.in", "r", stdin );
        freopen ( "bamboo.out", "w", stdout );
        scanf ( "%d%I64d", &n, &k );
        for ( int i = 1; i <= n; i ++ )    {
            scanf ( "%I64d", &a[i] );
            split ( a[i], vc );
        }
        sort ( vc.begin ( ), vc.end ( ) );
        vc.erase ( unique ( vc.begin ( ), vc.end ( ) ), vc.end ( ) );
        vc.push_back ( 10000000000000ll );
        ll ans = 1;
        for ( int i = 0; i + 1 < vc.size ( ); i ++ ) {
            ll l = vc[i]; ll r = vc[i+1] - 1;
            ll sum = 0;
            if ( !check ( l ) ) continue;
            while ( l <= r ) {
                int mid = ( l + r ) >> 1;
                if ( check ( mid ) ) {
                    sum = mid; l = mid + 1;
                } else r = mid - 1;
            }
            ans = max ( ans, sum );
        }
        printf ( "%I64d", ans );
        return 0;
    }
  • 相关阅读:
    NDK开发,如何配置 debug环境
    NDK 开发中,各种指令集的坑,arm64
    levmar ndk 编译
    kubernetes 存储
    kubernetes 存储
    Docker 仓库
    docker 容器
    查看系统日志
    linux 进程命令小结
    DaemonSet
  • 原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9533769.html
Copyright © 2011-2022 走看看