[九省联考2018] iiidx

给定一颗 n n 个点的有根树,和 n n 权值 d1...dn d 1 . . . d n 。将每个权值分配个每个节点,要求使父亲的权值小于等于儿子的权值。依次最大化 1,2,3...n 1 , 2 , 3... n 号点的权值,输出方案。

n600000 n ≤ 600000


1 1 号点开始枚举,由于我们要使一个点最大,所以我们每次都要求出当前符合要求的最大的权值。

di d i 的顺序与答案无关,所以我们可以先将 di d i 按从大到小的顺序排序

如何判断一个权值是符合要求的?
当且仅当有 sizei1 s i z e i − 1 个大与等于它的权值还没有被选中

我们设 fi f i 代表有多少个满足 ij i ≤ j dj d j 的权值还没有被选中,这样一个权值是符合要求的,当且仅当 sizeifi s i z e i ≤ f i

接下来我们只需要考虑如何维护 fi f i 就可以解决问题。

我们发现如果选择了一个点 di d i ,那么他子树中的点一定都会选择大于等于 di d i 的点。这时对于其他子树上的点,就相当于有 sizei1 s i z e i − 1 个大于等于 di d i 的点和一个 di d i 被取走了,即

f[i,n]sizei f [ i , n ] − s i z e i

当然,我们在枚举它的子树时要把它的贡献加回来,不过注意不要把它自己也加回来了。

每次查找时,先查找符合条件的最大值,然后再找到当前值最右侧的部分进行修改。

#include 
using namespace std;
const int N = 600000;

int n, d[N], a[N], ed[N]; double k;

struct segment_tree {
    int _min[N<<2], opt[N<<2];
    #define lson (o<<1)
    #define rson (o<<1|1)
    inline void pushup( int o ) { _min[o] = min( _min[lson], _min[rson] ); }
    inline void pushdown( int o )
    {
        if( !opt[o] ) return;
        _min[lson] += opt[o]; opt[lson] += opt[o];
        _min[rson] += opt[o]; opt[rson] += opt[o];
        opt[o] = 0;
    }
    inline void build( int o, int l, int r )
    {
        if( l == r ) {
            _min[o] = l; return;
        }
        int mid = (l+r) >>1;

        build( lson, l, mid );
        build( rson,mid+1,r );
        pushup( o );
    }
    inline void modify( int o, int l, int r, int ql, int qr, int val )
    {
        if( ql <= l && r <= qr ) {
            _min[o] += val; opt[o] += val; return;
        }
        pushdown( o ); int mid = (l+r) >>1;

        if( ql <= mid ) modify( lson, l, mid, ql, qr, val );
        if( mid+1<=qr ) modify( rson,mid+1,r, ql, qr, val );
        pushup( o );
    }
    inline int find( int o, int l, int r, int size )
    {
        if( l == r ) return size <= _min[o] ? l : l +1;
        pushdown( o ); int mid = (l+r) >>1;

        if( size <= _min[rson] ) return find( lson, l, mid, size );
        return find( rson, mid+1, r, size );
    }
    #undef lson
    #undef rson
} seg;

bool cmp( int a, int b ) { return a > b; }

int fa[N], size[N]; vector <int> son[N];

void calc_size( int u )
{
    size[u] = 1;
    for( int i = 0; i< son[u].size(); i ++ )
    {
        int v = son[u][i];

        calc_size( v );
        size[u] += size[v];
    }
}

int main()
{
    scanf( "%d%lf", &n, &k );
    for( int i = 1; i <= n; i ++ ) 
        scanf( "%d", &d[i] );

    sort( d+1, d+1+n, cmp );
    for( int i = 1; i <= n; i ++ )
    {
        fa[i] = i / k;
        son[fa[i]].push_back( i );
    }

    for( int i = n; i >= 1; i -- )
        ed[i] = ( d[i] == d[i+1] ) ? ed[i+1] +1 : 0;

    calc_size( 0 );

    seg.build( 1, 1, n );

    for( int i = 1; i <= n; i ++ )
    {
        if( fa[i] && fa[i] != fa[i-1] )
            seg.modify( 1, 1, n, a[fa[i]], n, size[fa[i]]-1 );

        int pos = seg.find( 1, 1, n, size[i] );
        pos += ed[pos]; // 找到当前权值最后的位置
        ed[pos] ++; // 选择该权值的数量 +1
        pos -= ed[pos] - 1; // 找到这次选择的位置
        a[i] = pos; // 记录答案
        seg.modify( 1, 1, n, pos, n, -size[i] );
    }

    for( int i = 1; i <= n; i ++ )
        printf( "%d ", d[a[i]] ); printf( "\n" ); 

    return 0;
}

你可能感兴趣的:(各省省选,2018,线段树)