瑶瑶的动感光波(加强版)
Time Limit:26000/13000MS (Java/Others)
Memory Limit:256000/128000 KB (Java/Others)
瑶瑶(tsyao)最近在练功—动感光波(适厉害呀(有错别字?)),瑶瑶有一个很大的练功房(其实是以前练舞蹈的么 ),练功房很大,可以分成n个区域。为了方便练功,练功房的布置很特别,所以我们可以把练功房看做以1号区域为根节点的,有n个节点的树。练功房每个区域有着不同的状态,有的区域雾气比较大,发挥动感光波的威力会消耗较大功力,有的区域很厉害,能够发挥动感光波的极大威力QAQ( 惊呆了)。每个区域都会有雾气ai,雾气会消耗ai的功力,能够发挥的威力bi这两个属性。瑶瑶的动感光波更厉害了,能够分成两个叉(每次使用动感光波时只能分叉一次),分别攻击不同的区域,当然,也可以不分叉。瑶瑶每次会选择一个区域z,然后再选择另外两个区域x和y,向x,y发出动感光波。这里,瑶瑶选择的z一定是x和y的祖先。
由于瑶瑶偷懒,不喜欢练功,因此她功力有限,每次发动动感光波途径一些区域时,会纠结是否在改区域消耗功力来获取威力(伤害?)。
给出m个查询,每次输入z,x,y,w四个整数,其中w为使用的功力。萌萌的瑶瑶想知道每次能够发挥最大为多少的威力。
第一行 两个整数n,m 。
接下来n行,每行两个整数ai,bi,表示第i号区域的两个属性。
接下来n-1行,每行两个整数ci,di,表示区域ci与区域di相接。
接下来m行,每行四个整数z,x,y,w。
9 1
50 100
1 1
30 1
30 1
1 1
50 100
1 1
50 100
30 1
1 2
2 3
3 4
3 5
3 6
4 7
4 8
5 9
2 7 9 31
3
图片为样例解释:
对于 100%的数据,有1 ≤ n ≤ 3000 , 1 ≤ m ≤ 10000 , 1 ≤ ai ≤ 50 , 1 ≤ bi ≤ 1000 , 1 ≤ x,y,z,ci,di ≤ n , 1 ≤ w ≤ 50 。
单组输入,共3组数据。
(由于出题人的疏忽,错误估算了暴力的时间复杂度,比赛时数据范围小了T_T,现在数据已更正:m≤1000 → m≤10000)
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define clear(A, X) memset (A, X, sizeof A) const int maxE = 6005; const int maxN = 3005; const int maxM = 55; struct Edge { int v, n; } edge[maxE]; int adj[maxN], cntE; int pre[maxN]; int n, m, V; int cost[maxN], val[maxN]; int vis[maxN]; int d[maxN][14][M]; int lca[maxN][maxN], p[maxN]; int deepth[maxN]; int f1[maxM], f2[maxM]; int find (int x) { return p[x] == x ? x : (p[x] = find (p[x])); } int Max (const int X, const int Y) { if (X > Y) return X; return Y; } void addedge (int u, int v) { edge[cntE].v = v; edge[cntE].n = adj[u]; adj[u] = cntE ++; edge[cntE].v = u; edge[cntE].n = adj[v]; adj[v] = cntE ++; } void DFS (int u, int pa) { p[u] = u; vis[u] = 1; deepth[u] = deepth[pa] + 1; for (int i = adj[u]; ~i; i = edge[i].n) { int v = edge[i].v; if (v == pa) continue; pre[v] = u; DFS (v, u); p[v] = u; } for (int i = 1; i <= n; ++ i) { int v = i; if (vis[v]) { lca[u][v] = lca[v][u] = find (v); } } } void work () { int u, v, x, y, z; clear (adj, -1); cntE = 0; clear (vis, 0); clear (deepth, 0); for (int i = 1; i <= n; ++ i) scanf ("%d%d", &cost[i], &val[i]); for (int i = 1; i < n; ++ i) { scanf ("%d%d", &u, &v); addedge (u, v); } pre[1] = 0; deepth[0] = 0; DFS (1, 0); //for (int i = 1; i <= n; ++ i) for (int j = i + 1; j <= n; ++ j) printf ("lca(%d, %d) = %d\n", i, j, lca[i][j]); for (int i = 1; i <= n; ++ i) { int k = 1, count = 1, xx = i, cur = 0, amount = 0; while (xx) { for (int j = 50; j >= cost[xx]; -- j) d[i][cur][j] = Max (d[i][cur][j], d[i][cur][j - cost[xx]] + val[xx]); if (count == k) { amount += k; if (amount > n) break; k <<= 1; ++ cur; for (int j = 50; j >= 0; -- j) d[i][cur][j] = d[i][cur - 1][j]; } xx = pre[xx]; ++ count; } } for (int i = 1; i <= m; ++ i) { scanf ("%d%d%d%d", &z, &x, &y, &V); clear (f1, 0); clear (f2, 0); int lcaa = lca[x][y]; int ans = 0; while (x != pre[z]) { int deepx = deepth[x] - deepth[z] + 1; //printf ("(%d,%d) = %d\n", x, z, deepx); int k1 = 1, amount = 0, cur = 0; while ((k1 << 1) < deepx) k1 <<= 1, ++ cur; while (1) { for (int j = V; j >= 0; -- j) for (int k = 0; k <= j; ++ k) f1[j] = Max (f1[j], f1[j - k] + d[x][cur][k]); while (++ amount < k1) x = pre[x]; x = pre[x]; if (k1 == 1) break; amount = 0; deepx = deepth[x] - deepth[z] + 1; cur = 0; k1 = 1; while ((k1 << 1) < deepx) k1 <<= 1, ++ cur; } } while (y != lcaa) { int deepy = deepth[y] - deepth[lcaa]; if (deepy == 0) break; int k1 = 1, amount = 0, cur = 0; while ((k1 << 1) < deepy) k1 <<= 1, ++ cur; while (1) { for (int j = V; j >= 0; -- j) for (int k = 0; k <= j; ++ k) f2[j] = Max (f2[j], f2[j - k] + d[y][cur][k]); while (++ amount < k1) y = pre[y]; y = pre[y]; if (k1 == 1) break; amount = 0; deepy = deepth[y] - deepth[lcaa]; cur = 0; k1 = 1; while ((k1 << 1) < deepy) k1 <<= 1, ++ cur; } } for (int j = V; j >= 0; -- j) ans = Max (ans, f1[V - j] + f2[j]); printf ("%d\n", ans); } } int main () { //freopen ("room.in", "r", stdin); //freopen ("room.txt", "w", stdout); while (~scanf ("%d%d", &n, &m)) work (); return 0; }