hdu 6039 线段树+dfs序

来源:互联网 发布:mac无法导入手机照片 编辑:程序博客网 时间:2024/06/05 19:50
constroy has some gears, each with a radius. Two gears are considered adjacent if they meet one of the following conditions: 

1. They share a common edge (i.e. they have equal linear velocity). 

2. They share a common shaft (i.e. they have equal angular velocity). 

It is guaranteed that no pair of gears meets both of the above conditions. 

A series of continuous adjacent gears constitutes a gear path. There is at most one gear path between each two gears. 

Now constroy assigns an angular velocity to one of these gears and then asks you to determine the largest angular velocity among them. 

sd0061 thinks this problem is too easy, so he replaces some gears and then asks you the question again.
Input
There are multiple test cases (about 3030). 

For each test case: 

The first line contains three integers n,m,qn,m,q, the number of gears, the number of adjacent pairs and the number of operations. (0m<n105,0q105)(0≤m<n≤105,0≤q≤105) 

The second line contains nn integers, of which the ii-th integer represents riri, the radius of the ii-th gear. (ri{2λ0λ30})(ri∈{2λ∣0≤λ≤30}) 

Each of the next mm lines contains three integers a,x,ya,x,y, the xx-th gear and the yy-th gear are adjacent in the aa-th condition. (a{1,2},1x,yn,xy)(a∈{1,2},1≤x,y≤n,x≠y) 

Each of the next qq line contains three integers a,x,ya,x,y, an operation ruled in the following: (a{1,2},1xn,y{2λ0λ30})(a∈{1,2},1≤x≤n,y∈{2λ∣0≤λ≤30}) 

a=1a=1 means to replace the xx-th gear with another one of radius yy

a=2a=2 means to assign angular velocity yy to the xx-th gear and then determine the maximum angular velocity.
Output
For each test case, firstly output " Case #xx:" in one line (without quotes), where xxindicates the case number starting from 11, and then for each operation of a=2a=2, output in one line a real number, the natural logarithm of the maximum angular velocity, with the precision of 33 digits.
Sample Input
4 3 41 4 16 21 2 41 2 32 1 41 1 161 2 42 4 41 4 164 3 52 16 4 82 1 21 2 31 1 42 1 41 3 82 1 161 4 12 1 8
Sample Output
Case #1:1.386Case #2:2.7733.4662.773
题意:给你一些齿轮,并告诉这些齿轮的半径,这些齿轮有两种关系
1.线速度相同(边相连)
2.角速度相同(同轴)
然后有两种询问
1.x y 把齿轮的半径改为y
2.x y 给齿轮x一个角速度y,然后问你最大的角速度
思路:首先我们不知道齿轮的角速度,所以我们可以先找参照物然后先处理出相对速度,由于齿轮之间还存在不同的关系,所以我们在建立线段树的时候用题中给出的半径的数组的自然顺序建立线段树肯定是不可以的,我们可以根据齿轮之间的关系建立一个dfs序,并将该序存在一个一维数组中,用这个一维数组建立线段树
首先介绍一下dfs序:
给定一棵n个节点的树,m次查询,每次查询需要求出某个节点深度为h的所有子节点。对于这个问题如果试图去对每个节点保存所有深度的子节点,在数据大的时候内存会吃不消;或者每次查询的时候去遍历一遍,当数据大的时候,时间效率会非常低。此时如果使用dfs序维护树结构就可以轻松地解决这个问题。作为预处理,首先将将树的所有节点按深度保存起来,每个深度的所有节点用一个线性结构保存,每个深度的节点相对顺序要和前序遍历一致。然后从树的根节点进行dfs,对于每个节点记录两个信息,一个是dfs进入该节点的时间戳in[id],另一个是dfs离开该节点的时间戳out[id]。最后对于每次查询,求节点v在深度h的所有子节点,只需将深度为h并且dfs进入时间戳在in[v]和out[v]之间的所有节点都求出来即可,由于对于每个深度的所有节点,相对顺序和前序遍历的顺序以致,那么他们的dfs进入时间戳也是递增的,于是可以通过二分搜索求解。
在这个题目中我们定义三个数组in,mid,out,其中in[x]~mid[x]之间存的是与齿轮x共轴的齿轮的  mid[x]+1~out之间存的是与齿轮x共边的齿轮(数组里存的都是dfs序数组中的下标)
然后我们可以建立并维护线段树了,维护一个最大值,在维护一个相对值
ac代码:
#include<bits/stdc++.h>#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1#define Log(x) (int)log2(x)using namespace std;typedef long long LL;const int MX = 1e5 + 5;const int INF = 0x3f3f3f3f;struct Edge {    int v, w, nxt;} E[MX * 2];int head[MX], rear;int p[MX];int dfn[MX], in[MX], mid[MX], out[MX], tot;int w[MX], r[MX], mark[MX];int Max[MX << 2], add[MX << 2];void init(int n) {    for (int i = 1; i <= n; i++) {        head[i] = -1;        p[i] = i;        mark[i] = 0;    }    rear = tot = 0;}void add_edge(int u, int v, int w) {    E[rear].v = v;    E[rear].w = w;    E[rear].nxt = head[u];    head[u] = rear++;}void dfs(int u, int fa) {    dfn[++tot] = u;    in[u] = tot;    for (int i = head[u]; ~i; i = E[i].nxt) {        int v = E[i].v, op = E[i].w;        if (v == fa || op == 1) continue;        w[v] = w[u];//同轴的角速度相同        mark[v] = 1;//有同轴的mark值为1        dfs(v, u);    }    mid[u] = tot;    for (int i = head[u]; ~i; i = E[i].nxt) {        int v = E[i].v, op = E[i].w;        if (v == fa || op == 2) continue;        w[v] = w[u] + r[u] - r[v];//同边的满足w[v] +r[v]= w[u] + r[u],同边的mark值为0        dfs(v, u);    }    out[u] = tot;}void PushUP(int rt) {    Max[rt] = max(Max[rt << 1], Max[rt << 1 | 1]);}void PushDown(int rt) {    if (add[rt]) {        add[rt << 1] += add[rt];        add[rt << 1 | 1] += add[rt];        Max[rt << 1] += add[rt];        Max[rt << 1 | 1] += add[rt];        add[rt] = 0;    }}void build(int l, int r, int rt) {    add[rt] = 0;    if (l == r) {        Max[rt] = w[dfn[l]];        return;    }    int m = (l + r) >> 1;    build(lson);    build(rson);    PushUP(rt);}int query(int L, int R, int l, int r, int rt) {    if (L <= l && R >= r) return Max[rt];    PushDown(rt);    int m = (l + r) >> 1;    int ret = -INF;    if (L <= m) ret = max(ret, query(L, R, lson));    if (R > m) ret = max(ret, query(L, R, rson));    PushUP(rt);    return ret;}void update(int L, int R, int val, int l, int r, int rt) {    if (L <= l && R >= r) {        add[rt] += val;        Max[rt] += val;        return;    }    PushDown(rt);    int m = (l + r) >> 1;    if (L <= m) update(L, R, val, lson);    if (R > m) update(L, R, val, rson);    PushUP(rt);}int find(int x) {    return p[x] == x ? x : (p[x] = find(p[x]));}int main() {    int cas = 0, n, m, q;    while (~scanf("%d%d%d", &n, &m, &q)) {        printf("Case #%d:\n", ++cas);        init(n);        for (int i = 1; i <= n; i++) {            scanf("%d", &r[i]);            r[i] = Log(r[i]);        }        for (int i = 1, op, u, v; i <= m; i++) {            scanf("%d%d%d", &op, &u, &v);            add_edge(u, v, op);            add_edge(v, u, op);            int p1 = find(u), p2 = find(v);//建立一个森林            if (p1 != p2) p[p2] = p1;        }        for (int i = 1; i <= n; i++) {//建立dfs序            if (find(i) == i) {                w[i] = 0;                mark[i] = 1;                dfs(i, -1);            }        }        build(1, n, 1);        while (q--) {            int op, x, y, t;            scanf("%d%d%d", &op, &x, &y);            t = Log(y);            if (op == 1) {                if (mark[x] && mid[x] < out[x])                    update(mid[x] + 1, out[x], t - r[x], 1, n, 1);//同轴的更新后半段,增量为t - r[x]                else if (mark[x] == 0)                    update(in[x], mid[x], r[x] - t, 1, n, 1);//同边的更新前半部分,由w[v] = w[u] + r[u] - r[v]可得w[v] - w[u] = r[u] - r[v],增量为r[x]-t                r[x] = t;            }            else {                int rt = find(x);                int ans = t + query(in[rt], out[rt], 1, n, 1) - query(in[x], in[x], 1, n, 1);//t+最大值的相对值-x的相对值(存在参照量嘛,计算一下差值就可以了嘛)                printf("%.3f\n", ans * log(2.0));            }        }    }    return 0;}