小H和游戏——dfs序+树状数组 | 思维

题目链接:https://ac.nowcoder.com/acm/contest/5203/D

题目大意:

给你一棵n个节点的树,然后有q次轰炸,每次轰炸可以波及到与当前节点距离小于等于2的节点,并输出该次轰炸后当前节点的被轰炸次数。

题解:

解法一:树状数组(线段树)+dfs序

这个题先讲相对而言时间复杂度比较高的解法,但是比较好想,容易理解。

首先我们可以求出每个点的儿子编号的区间(类似于dfs序的操作)以及每个节点的编号。

求出区间后就简单了,考虑每次轰炸,只把贡献传给儿子和父亲,这样统计的时候查询当前节点的值(即轰炸儿子和父亲产生的贡献),父亲节点的值(即轰炸爷爷节点和兄弟节点产生的贡献)以及儿子区间的值(轰炸孙子节点产生的贡献)

经典的区间修改,区间查询操作,推荐用树状数组维护(常数小)

trick:有一部分多考虑了,需要减去,代码中有解释。

#pragma GCC optimize(2)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PI atan(1.0)*4
#define E 2.718281828
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair
#define mp make_pair
#define pb push_back
#define debug printf("ac\n");
using namespace std;
inline int read()
{
    int a=0,b=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
            b=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        a=(a<<3)+(a<<1)+c-'0';
        c=getchar();
    }
    return a*b;
}
const int INF = 0x3f3f3f3f;
const int N = 750005;
vector G[N];
int id[N],l[N],r[N],idx;
int son[N],pa[N];
ll c1[N],c2[N],a[N];
inline int lowbit(int x){return x&(-x);}
inline void _add(int x,int val){for(int i=x;i

解法二:思维

首先我们不难发现对一个节点产生贡献的节点有6种,自身,儿子,孙子,兄弟,父亲,爷爷。

父亲节点,爷爷节点比较好办,因为就一个父亲和爷爷。

那儿子和孙子节点怎么处理呢?先不考虑最难的兄弟节点

那就不处理呗,每次只是暴力地把贡献上传给父亲和爷爷。

这样对于父亲和爷爷来说,他们儿子和孙子的节点贡献事先已经求出来了。

然后考虑最难的兄弟节点的贡献。

我们设cnt[i][0/1/2]表示一次轰炸后,自己/儿子/孙子波及到i点且还剩距离j可以波及的轰炸的次数

兄弟的贡献,一定是保存在了cnt[fa[i]][1]当中的,不过,里面有一个是自己波及的,所以答案加上cnt[fa[i]][1]-cnt[x][2]即可

代码实现:

#pragma GCC optimize(2)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PI atan(1.0)*4
#define E 2.718281828
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair
#define pb push_back
#define debug printf("ac\n");
using namespace std;
inline int read()
{
    int a=0,b=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
            b=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        a=(a<<3)+(a<<1)+c-'0';
        c=getchar();
    }
    return a*b;
}
const int N = 1e6+7;
int cnt[N][3];
int fa[N];
int u,v,num,n,query;
int main(){
    n=read(),query=read();
    for(int i=1;i<=n-1;i++){
        u=read(),v=read();
        fa[v]=u;
    }
    while(query--){
        num=read();
        cnt[num][2]++;//自身
        cnt[fa[num]][1]++;cnt[fa[fa[num]]][0]++;//上传对父亲和爷爷的贡献
        int ans=cnt[num][2]+cnt[num][1]+cnt[num][0];//自身+儿子+孙子对当前节点的贡献
        ans+=cnt[fa[num]][2];//父亲节点的贡献
        ans+=cnt[fa[num]][1]-cnt[num][2];//兄弟节点的贡献
        ans+=cnt[fa[fa[num]]][2];//爷爷节点的贡献
        printf("%d\n",ans);
    }
    return 0;
}

 

你可能感兴趣的:(树状数组,杂记,树状数组,dfs序,思维)