HDU 6203ping ping ping(LCA+贪心)

题意:给出一棵树,然后给出一些点对,要求这些点对无法连通(可以毁坏u到v路上的点,也可以毁坏u,也可以毁坏v)。问最少毁坏多少个点。

解法:很容易想到,如果毁坏u和v的最近公共祖先,那么这条路是断的,而且u的子树上的点到v的子树上的点也会无法连通。但是单凭LCA是不够的,因为深度越大的应该优先解决,深度小的点无法影响深度大的点。所以我们要贪心一下,每次取出点对的LCA深度小的点对。如果这个点对u和v都没有标记过,那么我们就把他们的LCA的整个子树给标记了。如果标记过了,那么就不需要处理。

整理一下思路,首先围绕LCA,然后我们要做标记整个子树的操作,标号的话要用dfs序,修改的话肯定不能用暴力,太慢了,所以想到用线段树。

最后需要用上LCA + dfs序 + 线段树。

别人很多用的树状数组,但是我不会树状数组的区间更新,所以自己搞了搞线段树。代码也是超级长。。。

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
inline int read() {
    char ch = getchar();
    int f = 1, x = 0;
    while(!(ch >= '0' && ch <= '9')) {
		if(ch == '-')
			f = -1;
		ch = getchar();
	}
    while(ch >= '0' && ch <= '9') {
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
    return x * f;
}

const int Maxn = 1e4 + 5;
int val;

struct seg
{
	int l, r;
	long long sum, lazy;
}segtree[Maxn * 4];

void build(int node, int l, int r) {
    segtree[node].l = l;
	segtree[node].r = r;
	segtree[node].lazy = 0;
    if(l == r) {
    	segtree[node].sum = 0;
		return;
	} else {
		int mid = (l + r) >> 1;
	    build(node << 1, l, mid);
	    build(node << 1 | 1, mid + 1, r);
	    segtree[node].sum = segtree[node << 1].sum + segtree[node << 1 | 1].sum;
	}
}

void pushdown(int node) {
	if(segtree[node].lazy != 0) {
		segtree[node<<1].lazy = 1;
		segtree[node<<1|1].lazy = 1;
		segtree[node<<1].sum = 1;
		segtree[node<<1|1].sum = 1;
		segtree[node].lazy = 0;
	}
}

long long query(int node, int x, int y) {
	if(segtree[node].l == x && segtree[node].r == y) {
		return segtree[node].sum;
	}
	pushdown(node);
	int mid = segtree[node].l + segtree[node].r;
	long long res = 0;
	mid >>= 1;
	if(mid >= y)
		res += query(node << 1, x, y);
	else if(mid < x)
		res += query(node << 1 | 1, x, y);
	else {
		res += query(node << 1, x, mid);
		res += query(node << 1 | 1, mid + 1, y);
	}
	return res;
}

void update(int node, int x, int y) {
	if(segtree[node].l == x && segtree[node].r == y) {
		segtree[node].lazy = val;
		segtree[node].sum = val;
		return;
	}
	pushdown(node);
	int mid = segtree[node].l + segtree[node].r;
	mid >>= 1;
	if(mid >= y)
		update(node << 1, x, y);
	else if(mid < x)
		update(node << 1 | 1, x, y);
	else {
		update(node << 1, x, mid);
		update(node << 1 | 1, mid + 1, y);
	}
//	segtree[node].sum = segtree[node << 1].sum + segtree[node << 1 | 1].sum;
}

const int maxn = 1e4 + 5;
const int M = 20; //树的深度 
int dp[2 * maxn][M];  //这个数组记得开到2*maxn,因为遍历后序列长度为2*n-1
bool vis[maxn];
struct edge {
    int u, v, w, next;
} e[2 * maxn];
int tot, head[maxn];
int in[maxn], out[maxn];

struct Node {
	int u, v, lca, dep;
	Node(){}
	Node(int a, int b, int c, int d) {
		u = a, v = b, lca = c, dep = d;
	}
	bool operator < (const Node& t) const {
		if(dep < t.dep)
			return 1;
		return 0;
	}
};


inline void add(int u ,int v ,int w ,int &k) {
    e[k].u = u; e[k].v = v; e[k].w = w;
    e[k].next = head[u]; head[u] = k++;
    u = u ^ v; v = u ^ v; u = u ^ v;
    e[k].u = u; e[k].v = v; e[k].w = w;
    e[k].next = head[u]; head[u] = k++;
}

int ver[2 * maxn], R[2 * maxn], first[maxn], dir[maxn];
//  ver:节点编号   R:深度      first:首位  dir:距离

void dfs(int u ,int dep, int& num2) {
    vis[u] = true; ver[++tot] = u; first[u] = tot; R[tot] = dep; in[u] = ++num2;
    for(int k = head[u]; ~k; k = e[k].next)
        if( !vis[e[k].v] ) {
            int v = e[k].v , w = e[k].w;
            dir[v] = dir[u] + w;
            dfs(v, dep + 1, num2);
            ver[++tot] = u; R[tot] = dep;
        }
    out[u] = num2;
}

void ST(int n) {
    for(int i = 1; i <= n; i++)
        dp[i][0] = i;
    for(int j = 1; (1<<j) <= n; j++) {
        for(int i = 1; i + (1<<j) - 1 <= n; i++) {
            int a = dp[i][j-1] , b = dp[i + (1<<(j - 1))][j - 1];
            dp[i][j] = R[a] < R[b] ? a : b; //i - i + 2 ^ j - 1中深度最小的编号 
        }
    }
}

int RMQ(int l, int r) {
    int k = 0;
    while((1<<(k + 1)) <= r - l + 1)
        k++;
    int a = dp[l][k], b = dp[r - (1<<k) + 1][k]; //保存的是编号
    return R[a] < R[b] ? a : b;
}

int LCA(int u ,int v) {
    int x = first[u] , y = first[v];
    if(x > y) swap(x,y);
    int res = RMQ(x,y);
    return ver[res];
}

priority_queue <Node> que;
int main() {
#ifndef ONLINE_JUDGE 
    freopen("in.txt","r",stdin);
    //freopen("Out.txt","w",stdout);
#endif
    int n, q, num, num2;
    while(scanf("%d", &n) != EOF) {
    	memset(head, -1, sizeof(head));
	    memset(vis, false, sizeof(vis));
	    n++;
	    num = 0;
	    num2 = 0;
	    for(int i = 1; i < n; i++) {
	        int u = read() + 1, v = read() + 1, val = 1;
	        add(u, v, val, num);
	    }
	    tot = 0; 
		dir[1] = 0;
	    dfs(1, 1, num2);
	    ST(2 * n - 1);
	    scanf("%d", &q);
	    while(q--) {
	        int x = read() + 1, y = read() + 1;
			int lca = LCA(x, y);
			que.push(Node(x, y, lca, R[first[lca]]));
	    }
	    int ans = 0;
	    build(1, 1, n);
	    val = 1;
	    while(!que.empty()) {
	    	Node now = que.top();
	    	que.pop();
	    	int x = query(1, in[now.u], in[now.u]), y = query(1, in[now.v], in[now.v]);
	    	if(x || y) {
	    		continue;
			} else {
				update(1, in[now.lca], out[now.lca]);
				ans++;
			}
		}
		printf("%d\n", ans);
	}
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值