BZOJ 5293: [Bjoi2018]求和 树链剖分+鬼畜代码

5293: [Bjoi2018]求和

>原题链接<

Description

master 对树上的求和非常感兴趣。他生成了一棵有根树,并且希望多次询问这棵树上一段路径上所有节点深度的k
 次方和,而且每次的k 可能是不同的。此处节点深度的定义是这个节点到根的路径上的边数。他把这个问题交给
了pupil,但pupil 并不会这么复杂的操作,你能帮他解决吗?

Input

第一行包含一个正整数n ,表示树的节点数。
之后n-1 行每行两个空格隔开的正整数i,j ,表示树上的一条连接点i 和点j 的边。
之后一行一个正整数m ,表示询问的数量。
之后每行三个空格隔开的正整数i,j,k ,表示询问从点i 到点j 的路径上所有节点深度的k 次方和。
由于这个结果可能非常大,输出其对998244353 取模的结果。
树的节点从1 开始标号,其中1 号节点为树的根。

Output

对于每组数据输出一行一个正整数表示取模后的结果。
1≤n,m≤300000,1≤k≤50

Sample Input

5
1 2
1 3
2 4
2 5
2
1 4 5
5 4 45

Sample Output

33
503245989

思路:

  因为k很小, 所以我们对50个次方都预处理一下答案, 用dis数组表示, 最后的a->b的答案就是dis(a)+dis(b)-dis(fa[lca(a, b)]);;

本篇代码采用鬼畜数组表达方式/指针邻接表存边。码风清奇,邀您一看!

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cctype>
#define ll long long
#define GG puts("Fuck!")
#define max(a, b) (a>b?a:b)
using namespace std;
const int N = 310000, K = 60;
const int mod = 998244353;
inline char Nc() {
    static char Buf[100000], *p1, *p2;
    return p1==p2&&(p2=(p1=Buf)+fread(Buf, 1, 100000, stdin), p1==p2)?EOF:*p1++;
}
int Rd() {
    int x = 0;char c = Nc();
    while(!isdigit(c)) c=Nc();
    while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48),c=Nc();
    return x;
}
int dep[N], top[N], fa[N], son[N], siz[N], maxinum, dis[N][K];
int pwr[N][K];
struct Edge {
    int to;
    Edge *next;
}*h[N], e[N<<1];
int _, n;
void Add_Edge(int u, int v) {
    Edge *tmp = &(++_)[e];
    tmp -> to = v;
    tmp -> next = u[h];
    h[u] = tmp;

    tmp = &(++_)[e];
    tmp -> to = u;
    tmp -> next = h[v];
    v[h] = tmp;
}
void Dfs1(int p) {
    dep[p] = p[fa][dep] + 1;
    for(int i=1;i<=50;i++)i[p[dis]] = (i[p[fa][dis]] + pwr[dep[p]-1][i])%mod;
    p[siz] = 1;
    maxinum = max(maxinum, p[dep]);
    for(Edge *i = p[h];i;i=i->next) {
        int to = i->to;
        if(to!=p[fa]) {
            to[fa] = p;
            Dfs1(to);
            p[siz] += to[siz];
            if(to[siz] > p[son][siz]) p[son] = to;    
        }
    }
}
void Dfs2(int p, int t) {
    p[top] = t;
    if(p[son]) Dfs2(son[p], t);
    for(Edge *i = p[h];i;i=i->next) {
        int to = i -> to;
        if(to!=p[fa]&&to!=p[son]) {
            Dfs2(to, to);
        }
    }
}
void Test() {
    /*for(int i=1;i<=5;i++) printf("%d ", i[dep]);
    puts("Fuck");*/
    GG;
    for(Edge *i = 2[h];i;i=i->next) {
        int to = i -> to;
        printf("%d\n", to);
    }
    GG;
}
void Init() {
    n=Rd();
    for(int i=2;i<=n;i++) {
        int u=Rd(), v=Rd();
        Add_Edge(u, v);
    }
    for(int i=1;i<=n;i++) {
        0[i[pwr]] = 1;
        for(int j=1;j<=50;j++) {
            j[i[pwr]] = ((ll)(j-1)[i[pwr]] * i)%mod;
        }
    }
    /*for(int i=1;i<=n;i++) {
        for(int j=1;j<=50;j++) {
            printf("%d %d %d\n", i, j, pwr[i][j]);
        }
    }*/
    Dfs1(1);
    Dfs2(1, 1);
    //Test();
}
void solve(int x, int y, int k) {
    int ret = 0;
    ret += (k[x[dis]]+ k[y[dis]]) %mod;
    while(x[top] != y[top]) {
        if(x[top][dep] < y[top][dep]) swap(x, y);
            x = x[top][fa];
        //printf("**%d**\n", x);
    }
    int ans = (x[dep] < y[dep]) ? x : y;
    //printf("--%d--\n", ans);
    ret -= k[ans[dis]];
    ret -= k[ans[fa][dis]];
    ((ret%=mod)+=mod)%=mod;
    printf("%d\n", ret);
}
int main() {
    Init();
    int q = Rd();
    while(q--) {
        int x=Rd(), y=Rd(), k=Rd();
        solve(x, y, k);
    }
}
打开新世界!

 

posted @ 2018-06-29 14:27  TOBICHI_ORIGAMI  阅读(101)  评论(1编辑  收藏  举报