您的位置:首页 > 理论基础 > 计算机网络

HDU 5044(2014 ACM-ICPC上海网络赛)

2014-10-07 16:59 232 查看
题意:给定一个树形图,节点10^5,有两种操作,一种是把某两点间路径(路径必定唯一)上所有点的权值增加一个固定值。

另一种也是相同操作,不同的是给边加权值。操作次数10^5。求操作过后,每个点和每条边的权值。

分析:此题时间卡得非常紧,最好用输入外挂,最好不要用RMQ来求解LCA。

此题是典型的在线LCA问题,先讲讲在线LCA要怎么做。

在线LCA有两种方法,第一种比较常见,即将其转化成RMQ问题。

先对树形图进行深度优先遍历,遍历过程记录路线中点的途经序列,每个非叶子节点会在序列中出现多次,从一个节点A的一个子节点回到A点再走另一个子节点的时候要再次加A加入序列。

记录序列的同时还要记录序列中每个点在树中对应的深度。以及在序列中第一次出现的位置(其实不一定非要第一个才行),主要用于根据点标号查找其在序列中对应的下标。

此时,LCA已经转化为RMQ,如果要求a,b的LCA,只需要找到a,b在遍历序列中分别对应的位置,并在深度序列中查找以这两点为端点的区间内的最小值即可。这个最小值在遍历序列中对应的点就是他们的LCA。

这种方法预处理O(NlogN),查询是O(1)。

模板如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

#define MAX_NODE_NUM 100005
#define MAX_EDGE_NUM MAX_NODE_NUM * 2
#define MAX_Q_LEN MAX_NODE_NUM
#define M 30
#define D(x)

struct Edge
{
int v, next, id;
Edge()
{}
Edge(int v, int next, int id):v(v), next(next), id(id)
{}
} edge[MAX_EDGE_NUM];

int head[MAX_NODE_NUM];
int edge_cnt;

void init_edge()
{
memset(head, -1, sizeof(head));
edge_cnt = 0;
}

void add_edge(int u, int v, int id)
{
edge[edge_cnt] = Edge(v, head[u], id);
head[u] = edge_cnt++;
}

int node_num, opr_num;
long long edge_opr[MAX_NODE_NUM];
long long node_opr[MAX_NODE_NUM];
bool vis[MAX_NODE_NUM];
long long ans_edge[MAX_EDGE_NUM];
int father[MAX_NODE_NUM][M];
int depth[MAX_NODE_NUM];
int seq2[MAX_NODE_NUM];
int seq2_cnt;

template<typename T>
class queue
{
T data[MAX_Q_LEN];
int head, rear;

public:
queue()
{
head = rear = 0;
}

bool empty()
{
return head == rear;
}

void pop()
{
head++;
if (head >= MAX_Q_LEN)
head = 0;
}

void push(T a)
{
data[rear++] = a;
if (rear >= MAX_Q_LEN)
rear = 0;
}

T front()
{
return data[head];
}
};

void bfs(int root)
{
queue<int> q;
q.push(root);
seq2_cnt = 0;
while (!q.empty())
{
int u = q.front();
q.pop();
vis[u] = true;
seq2[seq2_cnt++] = u;
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if (vis[v])
{
continue;
}
father[v][0] = u;
depth[v] = depth[u] + 1;
q.push(v);
}
}
}

//index start from 1.
void init_LCA(int root)
{
fill_n(vis, node_num + 1, 0);
memset(father, 0, sizeof(father));
bfs(root);
bool did;
for (int i = 1; i < M; i++)
{
did = false;
for (int j = 1; j <= node_num; j++)
{
int k = father[j][i - 1];
if (k <= 0)
{
continue;
}
father[j][i] = father[k][i - 1];
did = true;
}
if (!did)
{
break;
}
}
}

int LCA(int x, int y)
{
if (depth[x] > depth[y])
{
swap(x, y);
}
int diff = depth[y] - depth[x];
for (int i = 0; i < M && diff; i++)
{
if (diff & 1)
{
y = father[y][i];
}
diff >>= 1;
}
if (x == y)
{
return x;
}
int exp = 0;
while (x != y)
{
if (!exp || father[x][exp] != father[y][exp])
{
x = father[x][exp];
y = father[y][exp];
exp++;
}else
{
exp--;
}
}
return x;
}

inline int read_int()
{
int num = 0;
int sign = 1;
bool skip = false;
int c = 0;
while((c = getchar()) != EOF)
{
if(c == '-')
{
sign = -1;
skip = true;
}
else if(c >= '0' && c <= '9')
{
num = num * 10 + c - '0';
skip = true;
}
else if(skip)
{
break;
}
}
return num * sign;
}

inline int ReadOP()
{
int c = 0;
while((c = getchar()) != EOF && c != 'A');
getchar(); getchar();
return getchar();
}

void input()
{
scanf("%d%d", &node_num, &opr_num);
for (int i = 0; i < node_num - 1; i++)
{
int a, b;
a = read_int();
b = read_int();
add_edge(a, b, i);
add_edge(b, a, i);
}
init_LCA(1);
fill_n(edge_opr, node_num + 1, 0);
fill_n(node_opr, node_num + 1, 0);
fill_n(ans_edge, node_num + 1, 0);
for (int i = 0; i < opr_num; i++)
{
int a, b, k;
int op = ReadOP();
a = read_int();
b = read_int();
k = read_int();
int c = LCA(a, b);
D(printf("%d\n", c));
if (op == '2')
{
edge_opr[c] -= k * 2;
edge_opr[a] += k;
edge_opr[b] += k;
}else
{
node_opr[c] -= k;
if (father[c][0] > 0)
{
node_opr[father[c][0]] -= k;
}
node_opr[a] += k;
node_opr[b] += k;
}
}
}

void work()
{
for (int i = seq2_cnt - 1; i >= 0; i--)
{
int u = seq2[i];
D(printf("%d %lld\n", u, node_opr[u]));
for (int j = head[u]; j != -1; j = edge[j].next)
{
int v = edge[j].v;
if (v == father[u][0])
{
continue;
}
node_opr[u] += node_opr[v];
edge_opr[u] += edge_opr[v];
ans_edge[edge[j].id] = edge_opr[v];
}
D(printf("%d %lld\n", u, node_opr[u]));
}
}

void output()
{
bool first = true;
for (int i = 1; i <= node_num; i++)
{
if (first)
{
first = false;
}else
{
putchar(' ');
}
printf("%lld", node_opr[i]);
}
puts("");
first = true;
for (int i = 0; i < node_num - 1; i++)
{
if (first)
{
first = false;
}else
{
putchar(' ');
}
printf("%lld", ans_edge[i]);
}
puts("");
}

int main()
{
int t;
scanf("%d", &t);
for (int i = 0; i < t; i++)
{
printf("Case #%d:\n", i + 1);
init_edge();
seq2_cnt = 0;
input();
work();
output();
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: