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
1 2
1 3
2 4
2 5
2
1 4 5
5 4 45
Sample Output
33
503245989
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); } }