您的位置:首页 > 大数据 > 人工智能

[贪心+DFS序列维护树上前缀和]2014 Multi-University Training Contest 5 - 1002 Paths on the tree

2014-08-07 00:23 549 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4912

题目大意:给定一颗树和树上的m条路径,询问最多取多少两两不相交的路径

思路:若两路径相交,一定存在公共的LCA,比赛时考虑匹配,DP之类的思路,没有结果,官方解法是很巧妙的贪心:

把所有路径的LCA按深度从大到小排序,按这个顺序取所有互不相交的路径数就是答案。

若取了一条路径,则过与这条路径共LCA的路径都不能取。

按深度从大往小取,可以保证之前放好的路径不会影响到后面的路径,且每次留给后面的路径数最多。

具体实现先按LCA的深度排序,取路径时树状数组维护DFS序列。

若取一条路径,把DFS序中LCA所在的st[i]维护+1,出了LCA的节点en[i]+1维护-1,表示LCA所在的子树已经不能取路径(深度比该LCA大的路径已经取完,深度比LCA小的路径不能经过这颗子树)

判断能否取一条路径,计算路径两端点到LCA的前缀和是否为0(sum(st[A / B])- sum(st[ father[LCA] ])),为0表示路径不存在取过路径的子树

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std;
#define foru(i, a, b) for(int i=(a); i<=(b); i++)
#define ford(i, a, b) for(int i=(a); i>=(b); i--)
#define clr(a, b) memset(a, (b), sizeof(a))
typedef long long ll;
const double Pi = 4 * atan(1.0);

inline int readint() {
char c = getchar();
while(!isdigit(c)) c = getchar();
int ret = 0;
while(isdigit(c)) ret = ret * 10 + c - '0', c = getchar();
return ret;
}
/*************************************************************/
const int N = 100010;
struct Edge{
int a, b;
int lca;
}E
;

int n, m, tot, tim;
int adj
, next[2*N], aim[2*N], dep
, fa
;
int anc
[21], st
, en
, c
;

void add(int a, int b){
tot ++;
aim[tot] = b;
next[tot] = adj[a];
adj[a] = tot;
}

void init(){
tot = 0; clr(adj, 0);
foru(i, 1, n-1){
int u = readint();
int v = readint();
add(u, v);
add(v, u);
}
foru(i, 1, m) {
E[i].a = readint();
E[i].b = readint();
}
}

void dfs(int x, int pre){
st[x] = ++tim;
dep[x] = dep[pre] + 1;
fa[x] = anc[x][0] = pre;
int k = adj[x];
while (k){
int tx = aim[k];
if (tx != pre) dfs(tx, x);
k = next[k];
}
en[x] = tim;
}

int swim(int x, int H){
for(int i = 0; H; i++){
if (1&H) x = anc[x][i];
H >>= 1;
}
return x;
}

int LCA(int x, int y){
if (dep[x] < dep[y]) swap(x, y);
x = swim(x, dep[x] - dep[y]);
if (x == y) return x;
ford(i, 20, 0)
if (anc[x][i] != anc[y][i]){
x = anc[x][i];
y = anc[y][i];
}
return anc[x][0];
}

bool cmp(Edge x, Edge y){
return dep[x.lca] > dep[y.lca];
}

void prepare(){
tim = dep[1] = 0; dfs(1, 1); fa[1] = 0;
foru(j, 1, 20) foru(i, 1, n)
anc[i][j] = anc[anc[i][j-1]][j-1];
foru(i, 1, m)
E[i].lca = LCA(E[i].a, E[i].b);
sort(E+1, E+1+m, cmp);
}

int lowbit(int x){
return x&(-x);
}

void modify(int k, int x){
while (k < n){
c[k] += x;
k += lowbit(k);
}
}

int sum(int k){
int s = 0;
while (k > 0){
s += c[k];
k -= lowbit(k);
}
return s;
}

int ans;
void solve(){
ans = 0; clr(c, 0);
foru(i, 1, m){
int a = E[i].a;
int b = E[i].b;
int lca = E[i].lca;
if (sum(st[a]) - sum(st[fa[lca]]) != 0) continue; // ? fa[lca]
if (sum(st[b]) - sum(st[fa[lca]]) != 0) continue;
ans ++;
modify(st[lca], 1);
modify(en[lca]+1, -1);
}
printf("%d\n", ans);
}

int main(){
freopen("1002.txt", "r", stdin);
while (scanf("%d %d", &n, &m) != EOF){
init();
prepare();
solve();
}
return 0;
}


Tips :

DSF序列+树状数组可维护某节点到根的前缀和 -> 子树的st[i]维护+1,en[i]+1维护-1;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐