P8290 [省选联考 2022] 填树
先考虑第一问:
令 为权值在 中的答案。
注意到当极差为 时贡献会计算 次,利用这个特点答案可以表示为 , 下面不妨省去 。
一条路径上的 为所有结点 的积,我们单独考察一下:
也就是说,值域被 划分为若干区间,且每个区间上的 为一个不超过 次的多项式。
那么 应为一个不超过 次的多项式。
这样的划分点有 个,每个区间我们利用 换根dp 求出前 项然后插值即可。
下面介绍一下换根 dp 的方法,这也是 40 分做法:
首先钦定 为根,令 表示以 子树内的点作为起点, 的值,易得:
与此同时,我们需要知道每个点为起点时所有路径的权值和 ,有:
注意路径被计算了两次,单点只被计算了一次,所以有:
再考虑第二问,由于权值的贡献为和的形式所以我们考虑对每一个点单独计算贡献。
类似 , 定义 ,含义即为 在 中的取值之和。
再定义 表示经过 的路径条数(需要确定其他点权值),那么答案可以写作:
的定义与 的差异在于 的权值已经被钦定,为了对应定义
注意 的含义是以 为根的 ,那么有两种情况:
-
在以 为根的树中为 的儿子,此时
-
在以 为根的树中为 的父亲,此时
事实上 是 次式,那么 为 次式
, 为 次式。
总的时间复杂度即为 ,需要精细实现。
卡常方面技不如人,附上在 loj 上能过的代码:
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define pii pair< int , int >
#define fi first
#define sc second
#define mp make_pair
template<typename _T>
void read( _T &x ) {
x = 0; char s = getchar();
for( ; s < '0' || s > '9' ; s = getchar() );
for( ; s >= '0' && s <= '9' ; s = getchar() ) x = x * 10 + s - '0';
}
int pb , buf[ 100 ];
template<typename _T>
void write( _T x , char ed = '\n' ) {
if( x == 0 ) buf[ ++ pb ] = 0;
for( ; x ; x /= 10 ) buf[ ++ pb ] = x % 10;
for( ; pb ; pb -- ) putchar( buf[ pb ] + '0' );
putchar( ed );
}
const int MAXN = 205 , Mod = 1e9 + 7;
inline int Add( int x , int y ) { x += y; return x >= Mod ? x - Mod : x; }
inline int Sub( int x , int y ) { x -= y; return x < 0 ? x + Mod : x; }
inline int Mul( int x , int y ) { return 1ll * x * y % Mod; }
template<typename ...Args> inline int Mul( int x , Args ...args ) { return Mul( x , Mul( args... ) ); }
inline int Qkpow( int x , int po ) { int p = 1; for( ; po ; po >>= 1 , x = Mul( x , x ) ) if( po & 1 ) p = Mul( p , x ); return p; }
inline int Inv( int x ) { return Qkpow( x , Mod - 2 ); }
int fac[ MAXN + 5 ] , ivf[ MAXN + 5 ];
void Init( ) {
fac[ 0 ] = 1;
for( int i = 1 ; i <= MAXN ; i ++ ) fac[ i ] = Mul( fac[ i - 1 ] , i );
ivf[ MAXN ] = Inv( fac[ MAXN ] );
for( int i = MAXN ; i >= 1 ; i -- ) ivf[ i - 1 ] = Mul( ivf[ i ] , i );
}
pii operator + ( pii a , pii b ) { return mp( Add( a.fi , b.fi ) , Add( a.sc , b.sc ) ); }
pii operator - ( pii a , pii b ) { return mp( Sub( a.fi , b.fi ) , Sub( a.sc , b.sc ) ); }
int px[ MAXN + 5 ] , sx[ MAXN + 5 ];
int Lagrange( int n , int *y , int L , int x0 ) {
if( x0 - L + 1 <= n ) return y[ x0 - L + 1 ];
px[ 0 ] = 1; sx[ n + 1 ] = 1;
for( int i = 1 ; i <= n ; i ++ ) px[ i ] = Mul( px[ i - 1 ] , Sub( x0 , Add( L , i - 1 ) ) );
for( int i = n ; i >= 1 ; i -- ) sx[ i ] = Mul( sx[ i + 1 ] , Sub( x0 , Add( L , i - 1 ) ) );
int ans = 0;
for( int i = 1 ; i <= n ; i ++ )
ans = Add( ans , Mul( y[ i ] , px[ i - 1 ] , sx[ i + 1 ] , ivf[ i - 1 ] , ( n - i ) & 1 ? Sub( 0 , ivf[ n - i ] ) : ivf[ n - i ] ) );
return ans;
}
int n , K , l[ MAXN + 5 ] , r[ MAXN + 5 ];
vector< int > Graph[ MAXN + 5 ];
int gs , f[ MAXN + 5 ] , g[ MAXN + 5 ] , f2[ MAXN + 5 ] , g2[ MAXN + 5 ] , par[ MAXN + 5 ];
int V1[ MAXN + 5 ] , V2[ MAXN + 5 ];
void dfs1( int u , int fa ) {
f[ u ] = 1; par[ u ] = fa;
for( int v : Graph[ u ] ) if( v != fa )
dfs1( v , u ) , f[ u ] = Add( f[ u ] , f[ v ] );
f2[ u ] = f[ u ]; f[ u ] = Mul( f[ u ] , V1[ u ] );
}
void dfs2( int u , int fa ) {
gs = Add( gs , Add( g[ u ] , V1[ u ] ) );
for( int v : Graph[ u ] ) if( v != fa ) {
g[ v ] = Add( f[ v ] , Mul( Sub( g[ u ] , Mul( f[ v ] , V1[ u ] ) ) , V1[ v ] ) );
g2[ v ] = Add( f2[ v ] , Sub( g[ u ] , Mul( f[ v ] , V1[ u ] ) ) );
dfs2( v , u );
}
}
const int iv2 = Inv( 2 );
int m , d[ 4 * MAXN + 5 ] , y_1[ MAXN + 5 ] , y_2[ MAXN + 5 ];
pii Solve( int k ) {
m = 0;
for( int i = 1 ; i <= n ; i ++ )
d[ ++ m ] = l[ i ] - k , d[ ++ m ] = l[ i ],
d[ ++ m ] = r[ i ] - k , d[ ++ m ] = r[ i ];
sort( d + 1 , d + m + 1 ); m = unique( d + 1 , d + m + 1 ) - d - 1;
d[ m ] ++;
pii ans( 0 , 0 );
for( int i = 1 ; i < m ; i ++ ) { //[di,di+1)
int L = d[ i ] , R = d[ i + 1 ] - 1;
for( int j = L ; j <= min( R , L + n + 2 ) ; j ++ ) {
gs = 0;
for( int u = 1 ; u <= n ; u ++ )
V1[ u ] = max( 0 , min( j + k , r[ u ] ) - max( j , l[ u ] ) + 1 ),
V2[ u ] = max( 0 , (int)( 1ll * ( min( j + k , r[ u ] ) - max( j , l[ u ] ) + 1 ) * ( min( j + k , r[ u ] ) + max( j , l[ u ] ) ) / 2 % Mod ) );
dfs1( 1 , 0 ); g[ 1 ] = f[ 1 ]; g2[ 1 ] = f2[ 1 ]; dfs2( 1 , 0 );
y_1[ j - L + 1 ] = Add( y_1[ j - L ] , Mul( gs , iv2 ) );
y_2[ j - L + 1 ] = y_2[ j - L ];
for( int u = 1 ; u <= n ; u ++ ) if( V1[ u ] ) {
int S = Mul( g2[ u ] , g2[ u ] );
for( int v : Graph[ u ] ) {
int d;
if( v == par[ u ] ) d = Mul( Sub( g2[ v ] , f[ u ] ) , V1[ v ] );
else d = f[ v ];
S = Sub( S , Mul( d , d ) );
}
y_2[ j - L + 1 ] = Add( y_2[ j - L + 1 ] , Mul( V2[ u ] , Mul( S + 1 , iv2 ) ) );
}
}
ans.fi = Add( ans.fi , Lagrange( n + 2 , y_1 , L , R ) );
ans.sc = Add( ans.sc , Lagrange( n + 3 , y_2 , L , R ) );
}
return ans;
}
int main( ) {
// freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout);
Init();
read( n ); read( K );
for( int i = 1 ; i <= n ; i ++ ) read( l[ i ] ) , read( r[ i ] );
for( int i = 1 , u , v ; i < n ; i ++ ) {
read( u ); read( v );
Graph[ u ].push_back( v );
Graph[ v ].push_back( u );
}
pii ans = Solve( K ) - Solve( K - 1 );
printf("%d\n%d\n", ans.fi , ans.sc );
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?