-
1 3 1 2 2 1 2 3 1 2 1 1 1
样例输出
-
Case 1: 12348
描述
有一个N个节点的树,其中点1是根。初始点权值都是0。
一个节点的深度定义为其父节点的深度+1,。特别的,根节点的深度定义为1。
现在需要支持一系列以下操作:给节点u的子树中,深度在l和r之间的节点的权值(这里的深度依然从整个树的根节点开始计算),都加上一个数delta。
问完成所有操作后,各节点的权值是多少。
为了减少巨大输出带来的开销,假设完成所有操作后,各节点的权值是answer[1..N],请你按照如下方式计算出一个Hash值(请选择合适的数据类型,注意避免溢出的情况)。最终只需要输出这个Hash值即可。
MOD =1000000007; // 10^9 + 7
MAGIC= 12347;
Hash =0;
For i= 1 to N do
Hash = (Hash * MAGIC + answer[i]) mod MOD;
EndFor
输入
第一行一个整数T (1 ≤ T ≤ 5),表示数据组数。
接下来是T组输入数据,测试数据之间没有空行。
每组数据格式如下:
第一行一个整数N (1 ≤ N ≤ 105),表示树的节点总数。
接下来N - 1行,每行1个数,a (1 ≤ a ≤ N),依次表示2..N节点的父亲节点的编号。
接下来一个整数Q(1 ≤ Q ≤ 105),表示操作总数。
接下来Q行,每行4个整数,u, l, r, delta (1 ≤ u ≤ N, 1 ≤ l ≤ r ≤ N, -109 ≤ delta ≤ 109),代表一次操作。
输出
对每组数据,先输出一行“Case x: ”,x表示是第几组数据,然后接这组数据答案的Hash值。
数据范围
小数据:1 ≤ N, Q ≤ 1000
大数据:1 ≤ N, Q ≤ 105
样例解释
点1的子树中有1,2,3三个节点。其中深度在2-3之间的是点2和点3。
点2的子树中有2,3两个节点。其中没有深度为1的节点。
所以,执行完所有操作之后,只有2,3两点的权值增加了1。即答案是0 1 1。再计算对应的Hash值即可。
注意表示为father的时候每个结点的高度并不是可以直接得到的,需要深搜,先贴一下我的错误代码:
#include <stdio.h>
#include <vector>
#include <string.h>
#include <iostream>
#include <memory.h>
using namespace std;
int T, i, j, t, N, Q, u, l, r, delta, d[100002], answer[100002],father;
vector<int> tree[100002];
void dfs(int u, int l, int r, int delta) {
if (d[u] >= l && d[u] <= r)
answer[u] += delta;
for (int i = 0; i < tree[u].size(); ++i) {
dfs(tree[u][i], l, r, delta);
}
}
int main() {
freopen("E:\\练习\\testForVS2012\\test\\test\\in.txt","r",stdin);
const long long MOD =1000000007, MAGIC= 12347;
long long hash = 0;
scanf("%d",&T);
for (t = 1; t <= T; ++t) {
scanf("%d", &N);
memset(d, 0, sizeof(d));
memset(answer, 0, sizeof(answer));
hash = 0;
d[1] = 1;
for (i = 2; i <= N; ++i) {
scanf("%d",&father);
d[i] = d[father] + 1;
tree[father].push_back(i);
}
scanf("%d", &Q);
for (i = 0; i < Q; ++i) {
scanf("%d%d%d%d",&u, &l, &r, &delta);
dfs(u, l, r, delta);
}
for (i = 1; i <= N; ++i)
hash = (hash * MAGIC + answer[i]) % MOD;
printf("Case %d: %lld\n",t,hash);
for (i = 1; i <= N; ++i)
tree[i].clear();
}
return 0;
}
据说如果把上面的d[i] = d[father] + 1改掉我上面暴力的方法都可以通过,但是比较好的方法是用树状数组把查询都先记录下来,然后再深搜一次更新,补充一下树状数组的概念:
1
2
3
|
intlowbit(intx){
returnx&(x^(x–1));
}
|
1
2
3
|
intlowbit(intx){
returnx&(-x);
}
|
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN = 100000 + 100;
const int MOD = 1000000007;
int qlast[MAXN], last[MAXN];
struct QQ{
int l, r, delta;
int pre;
} q[MAXN];
struct EE {
int y;
int pre;
} e[MAXN];
long long ans[MAXN];
long long hash1;
long long t[MAXN];
int n;
void add(int d, int del)
{
if (d < 1)
return;
d = n - d + 1;
while (d <= n)
{
t[d] += del;
d += (d & (-d));
}
}
long long getsum(int d)
{
d = n - d + 1;
long long ret = 0;
while (d)
{
ret += t[d];
d -= (d & (-d));
}
return ret;
}
void solve(int x, int d)
{
int i = qlast[x];
while (i != 0)
{
add(q[i].r, q[i].delta);
add(q[i].l - 1, -q[i].delta);
i = q[i].pre;
}
ans[x] += getsum(d);
while (last[x] != 0)
{
solve(e[last[x]].y, d + 1);
last[x] = e[last[x]].pre;
}
i = qlast[x];
while (i != 0)
{
add(q[i].r, -q[i].delta);
add(q[i].l - 1, q[i].delta);
i = q[i].pre;
}
}
int main()
{
int T;
freopen("E:\\练习\\testForVS2012\\test\\test\\in.txt","r",stdin);
scanf("%d", &T);
for (int tt = 1; tt <= T; ++tt)
{
for (int i = 1; i <= n; ++i)
{
t[i] = 0;
ans[i] = 0;
qlast[i] = 0;
last[i] = 0;
}
scanf("%d", &n);
for (int i = 2; i <= n; ++i)
{
int p;
scanf("%d", &p);
e[i].y = i;
e[i].pre = last[p];
last[p] = i;
}
int Q;
scanf("%d", &Q);
for (int i = 1; i <= Q; ++i)
{
int x;
scanf("%d%d%d%d", &x, &q[i].l, &q[i].r, &q[i].delta);
q[i].pre = qlast[x];
qlast[x] = i;
}
solve(1, 1);
printf("Case %d: ", tt);
int magic = 12347;
hash1 = 0;
for (int i = 1; i <= n; ++i)
{
hash1 = (hash1 * magic + ans[i]) % MOD;
}
cout << hash1 << endl;
}
return 0;
}