• [湖南集训]更为厉害/谈笑风生


    题目

      点这里看题目。

    分析

      首先发现,对于((a,b,c))的合法三元组,(c)一定在(a)的子树内,并且(b)也是(c)的祖先。那么我们只需要考虑(b)的位置。如果(b)(a)的子孙,那么(c)一定就是(b)的子孙,此时的贡献是(siz(b)-1)(我们以下都用(siz(u))表示(u)的子树大小)。如果(b)(a)的祖先,那么贡献就是(siz(a)-1)
      发现,当(b)(a)的祖先的时候,贡献只与(k)(siz(a))(a)的深度有关,因此可以直接计算。
      因此我们只需要考虑,对每个询问求出:

    [sum_{dis(p,v)le k} (siz(v)-1) ]

      这是一个长链剖分可做的问题。但是由于我很蒻,因此我不会。
      考虑到 " (a)(b)距离不超过(k) " 的限制,我们可以发现,(b)(a)的深度差不会超过(k)。因此,知道了(a)的深度和(k),我们就可以知道合法的(b)的最大的深度,我们称这个最大深度为一个询问的 " 可用深度 " 。
      因此,我们将所有点按照深度从小到大,插入贡献。对于一个询问,当深度小于等于它的 " 可用深度 " 的点都被插入后,我们就可以计算它此时的答案——也就是它子树内的贡献和。
      可以发现,此时查询既满足贡献不遗漏,也满足不会有多余的贡献。
      子树内的贡献和,可以通过将树展开为 DFS 序的方法,转化为区间求和,用 BIT 可以维护。点和询问都可以在排序后使用指针扫过去。
      时间复杂度(O((n+q)log_2n))

    代码

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    typedef long long LL;
    
    const int MAXN = 3e5 + 5;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0;char s = getchar();int f = 1;
    	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
    	while( s >= '0' && 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 ) + 1; }
    	if( 9 < x ){ write( x / 10 ); }
    	putchar( x % 10 + '0' );
    }
    
    template<typename _T>
    _T MIN( const _T a, const _T b )
    {
    	return a < b ? a : b;
    }
    
    template<typename _T>
    _T MAX( const _T a, const _T b )
    {
    	return a > b ? a : b;
    }
    
    struct edge
    {
    	int to, nxt;
    }Graph[MAXN << 1];
    
    struct Query
    {
    	int dd, id, A, K;
    	bool operator < ( const Query &b ) const { return dd < b.dd; }
    };
    
    Query q[MAXN];
    
    LL BIT[MAXN], ans[MAXN];
    int pts[MAXN];
    int head[MAXN], dep[MAXN], pos[MAXN], siz[MAXN];
    int N, Q, cnt, ID;
    
    int lowbit( const int x ) { return x & ( -x ); }
    void update( int x, int v ) { for( ; x <= N ; x += lowbit( x ) ) BIT[x] += v; }
    LL getSum( int x ) { LL ret = 0; while( x ) ret += BIT[x], x -= lowbit( x ); return ret; }
    LL query( int l, int r ) { return getSum( r ) - getSum( l - 1 ); }
    bool cmp( const int &x, const int &y ) { return dep[x] < dep[y]; }
    
    void addEdge( const int from, const int to )
    {
    	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
    	head[from] = cnt;
    }
    
    void DFS( const int u, const int fa )
    {
    	dep[u] = dep[fa] + 1, siz[u] = 1, pos[u] = ++ ID;
    	for( int i = head[u], v ; i ; i = Graph[i].nxt )
    		if( ( v = Graph[i].to ) ^ fa )
    			DFS( v, u ), siz[u] += siz[v];
    }
    
    signed main()
    {
    	int height = 0;
    	read( N ), read( Q );
    	for( int i = 1, a, b ; i < N ; i ++ ) read( a ), read( b ), addEdge( a, b ), addEdge( b, a );
    	DFS( 1, 0 );
    	for( int i = 1 ; i <= N ; i ++ ) height = MAX( height, dep[i] ), pts[i] = i;
    	for( int i = 1 ; i <= Q ; i ++ ) 
    		read( q[i].A ), read( q[i].K ), q[i].dd = MIN( dep[q[i].A] + q[i].K, height ), q[i].id = i;
    	std :: sort( q + 1, q + 1 + Q );
    	std :: sort( pts + 1, pts + 1 + N, cmp );
    	int rig = 1, ptr = 1;
    	for( int i = 1 ; i <= N ; )
    	{
    		for( ; rig <= N && dep[pts[rig]] == dep[pts[i]] ; rig ++ );
    		for( ; i < rig ; i ++ ) update( pos[pts[i]], siz[pts[i]] - 1 );
    		if( q[ptr].dd == dep[pts[i - 1]] )
    		{
    			for( ; ptr <= Q && q[ptr].dd == dep[pts[i - 1]] ; ptr ++ )
    			{
    				ans[q[ptr].id] = query( pos[q[ptr].A] + 1, pos[q[ptr].A] + siz[q[ptr].A] - 1 );
    				ans[q[ptr].id] += 1ll * MIN( q[ptr].K, dep[q[ptr].A] - 1 ) * ( siz[q[ptr].A] - 1 );
    			}
    		}
    	}
    	for( int i = 1 ; i <= Q ; i ++ ) write( ans[i] ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    简单区间dp
    【题解】石子合并
    【2019.7.6】刷题记录
    【题解】大朋友的数字
    【基础】dp系列1
    【题解】垃圾陷阱
    【题解】导弹拦截
    hadoop各组件安装(非专业人士,不定期更新)
    python逼格提升
    python第三十二天-----算法
  • 原文地址:https://www.cnblogs.com/crashed/p/12888206.html
Copyright © 2020-2023  润新知