Live2D

Solution -「ARC 125F」Tree Degree Subset Sum

\(\mathcal{Description}\)

  Link.

  给定含有 \(n\) 个结点的树,求非负整数对 \((x,y)\) 的数量,满足存在 \(\exist S\subseteq V,~|S|=x\land\sum_{u\in S}d_u=y\),其中 \(d_u\) 表示点 \(u\) 的度数。

  \(n\le2\times10^5\)

\(\mathcal{Solution}\)

  方便期间,以下所有 \(d_u\) 表示 \(u\) 的度数 \(-1\)

  出题人莫名其妙告诉你一棵树,无非是强调 \(\sum d=n-2\),自然想到根号分治。不过朴素 DP 的状态数量就已经难以接受,我们需要更多的结论。

  比如这个结论:

结论:若 \(x_1\le x_2\)\((x_1,y),(x_2,y)\) 均合法,那么 \(\forall x_3\in[x_1,x_2],~(x_3,y)\) 也合法。

证明   对于某个 $y$,取出最小 $x_l$ 和最大的 $x_r$,使得 $(x_l,y),(x_r,y)$ 合法。设 $\{d_n\}$ 中有 $z$ 个值为 $0$,则我们只需证明 $x_r-x_l\le 2z$,这是由于 $(x_l,y)$ 的选取中必然不含 $0$,那么 $(x_l+1,y),(x_l+2,y),\cdots,(x_l+z,y)$ 都合法,$(x_r-k,y)$ 同理。

  考虑任意一个 \(S\subseteq V\),令 \(d_S=\sum_{u\in S}d_u\),那么

  • \(d_S-|S|\ge -z\),显然;
  • \(d_S-|S|\le z-2\)\(d_S\le \sum d=n-2\),取等时 \(|S|\ge n-z\),得证。

  即 \(-z\le d_S-|S|\le z-2\),考虑将 \((x_l,y)\)\((x_r,y)\) 代入,有 \(-z\le y-x_r\le y-x_l\le z-2\),可以推出 \(x_r-x_l\le 2z-2\),故已有原命题成立。 \(\square\)

  所以,我们只需要对于每个 \(y\),DP 求出 \(x_l\)\(x_r\) 就能得到答案。DP 时根号分治,内部用单调队列优化即可。复杂度 \(\mathcal O(n\sqrt n)\)

\(\mathcal{Code}\)

/*~Rainybunny~*/

#include <bits/stdc++.h>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

typedef long long LL;

const int MAXN = 2e5;
int n, d[MAXN + 5], f[MAXN + 5], g[MAXN + 5];

inline void trans( const int v, const int c, const int r,
  int* h, const auto& cmp ) { // cmp(a,b) is true <=> a is the better value.
    static int que[MAXN + 5], th[MAXN + 5];
    int hd = 1, tl = 0;
    for ( int i = r; i <= n - 2; i += v ) {
        th[i] = h[i];
        while ( hd <= tl && que[hd] + c * v < i ) ++hd;
        while ( hd <= tl
          && cmp( th[i] - i / v, th[que[tl]] - que[tl] / v ) ) --tl;
        que[++tl] = i;
        h[i] = th[que[hd]] + ( i - que[hd] ) / v;
    }
}

int main() {
    scanf( "%d", &n );
    rep ( i, 1, n ) d[i] = -1;
    rep ( i, 2, n ) {
        int u, v; scanf( "%d %d", &u, &v );
        ++d[u], ++d[v];
    }

    std::sort( d + 1, d + n + 1 );
    memset( f, 0x3f, sizeof f ), memset( g, 0xc0, sizeof g );
    f[0] = g[0] = 0;
    for ( int l = 1, r; l <= n; l = r + 1 ) {
        for ( r = l; r < n && d[r + 1] == d[l]; ++r );
        int v = d[l], c = r - l + 1;
        if ( !v ) { g[0] = c; continue; }
        rep ( r, 0, v - 1 ) {
            trans( v, c, r, f,
              []( const int u, const int v ) { return u < v; } );
            trans( v, c, r, g,
              []( const int u, const int v ) { return u > v; } );
        }
    }

    LL ans = 0;
    rep ( i, 0, n - 2 ) {
        // printf( "%d: [%d,%d]\n", i, f[i], g[i] );
        if ( f[i] <= g[i] ) ans += g[i] - f[i] + 1;
    }
    printf( "%lld\n", ans );
    return 0;
}

posted @ 2021-08-23 18:40  Rainybunny  阅读(54)  评论(0编辑  收藏  举报