「ARC125F」Tree Degree Subset Sum
题目
点这里看题目。
分析
结论题什么的最恶心了😣
假如你并不知道这道题该怎么做,只能猜一点性质然后假装它是对的做下去,你会猜什么呢?
设第 \(i\) 个点的度数为 \(d_i\)。为了方便,这里将每个点的度数都减了 1,因此有 \(\forall i,0\le d_i<n-1\)。
考虑任意的 \(y\),我们不妨设 \(f_y\) 为凑出 \(y\) 所需的最小的 \(x\),相应地设 \(g_y\) 为凑出 \(y\) 所需的最大的 \(x\)。或许你现在可以猜到,对于 \(y\),可行的 \(x\),是不是就是所有的 \(f_y\le x\le g_y\) ?
随便手玩几组样例可以发现这似乎的确是对的。假如这是对的,那么我们就可以 DP 算出 \(f,g\)。对于相同的 \(d\),我们集中在一起做多重背包,由于 \(\sum d=n-2\),所以不同的 \(d\) 的数量为 \(O(\sqrt n)\),使用单调队列优化后复杂度就是 \(O(n\sqrt n)\)。最后统计答案就是简单的活计了。
吐槽一句,单调队列常数不小,实际上这个算法比二进制分组还要慢
尝试证明我们的结论。设 \(z\) 为 \(d_i=0\) 的 \(i\) 的个数,那么我们需要证明的结论可以转化为证明 \(\forall y,g_y-f_y\le 2z\)。这是因为,\(f_y\) 的组合方案中,一定没有 0,所以我们可以加入若干个 0,得到 \(x\in [f_y,f_y+z]\) 的组合方案;相应地,我们也可以得到 \(x\in[g_y-z,g_y]\) 的组合方案,因此该结论若成立,原结论也成立。
考虑任意一个从 \(n\) 个点中选取的子集 \(S\),设 \(s=\sum_{x\in S}d_x,c=|S|\),那么考虑 \(s-c\) 的上界和下界。
- 当 \(s\) 尽可能小的时候会取到下界,此时必然有 \(\forall x\in S,d_x=0\),因此 \(s-c\ge -z\)。
- 当 \(s\) 尽量大的时候会取到上界,此时 \(S\) 会包含所有 \(d_x-1>0\) 的 \(x\)。因而这个上界可以直接算出来。因为 \(\sum_{i=1}^nd_i-n=-2\),所以 \(\sum_{i=1}^n(d_i-1)[d_i>1]=z-2\);
因此得到不等式 \(-z\le s-c\le z-2\)。将 \(f,g\) 代入得到 \(-z\le s-g(S)\le s-f(S)\le z-2\),简单变形缩放一下得到 \(g(S)-f(S)\le 2z-2\le 2z\)。
小结:
- 需要加强找结论、猜结论的能力!
不会做总得会猜吧,瞎猜也行啊; - 注意一下利用 \(d\) 取值有限的性质,将 01 背包转化为多重背包的方法;
代码
#include <cstdio>
#include <cstring>
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int MAXN = 2e5 + 5;
template<typename _T>
void read( _T &x )
{
x = 0; char s = getchar(); int f = 1;
while( ! ( '0' <= s && s <= '9' ) ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
x *= f;
}
template<typename _T>
void write( _T x )
{
if( x < 0 ) putchar( '-' ), x = -x;
if( 9 < x ) write( x / 10 );
putchar( x % 10 + '0' );
}
int q[MAXN];
int f[MAXN], g[MAXN], tmp[MAXN];
int app[MAXN];
int deg[MAXN];
int N;
int main()
{
read( N );
rep( i, 1, N - 1 )
{
int a, b; read( a ), read( b );
deg[a] ++, deg[b] ++;
}
rep( i, 1, N ) app[deg[i] - 1] ++;
int h, t, n;
memset( f, 0x3f, sizeof f );
memset( g, 0xc0, sizeof g );
f[0] = g[0] = 0;
rep( i, 0, N - 2 )
{
if( ! app[i] ) continue;
rep( r, 0, i - 1 )
{
h = 1, t = 0;
n = ( N - 2 - r ) / i;
rep( j, 0, n )
{
while( h <= t && f[q[t] * i + r] - q[t] >= f[j * i + r] - j ) t --;
q[++ t] = j;
while( h <= t && q[h] < j - app[i] ) h ++;
tmp[j * i + r] = f[q[h] * i + r] - q[h] + j;
}
rep( j, 0, n ) f[j * i + r] = tmp[j * i + r];
h = 1, t = 0;
rep( j, 0, n )
{
while( h <= t && g[q[t] * i + r] - q[t] <= g[j * i + r] - j ) t --;
q[++ t] = j;
while( h <= t && q[h] < j - app[i] ) h ++;
tmp[j * i + r] = g[q[h] * i + r] - q[h] + j;
}
rep( j, 0, n ) g[j * i + r] = tmp[j * i + r];
}
}
LL ans = 0;
rep( i, 0, N )
if( f[i] <= g[i] )
ans += g[i] - f[i] + 1 + app[0];
write( ans ), putchar( '\n' );
return 0;
}