您的位置:首页 > 其它

更改点后输出把集合里的点通过树的边连在一起所需要的最小代价 LCA+树状数组 HDU 5296 Annoying problem

2015-08-24 18:20 441 查看


Annoying problem

Time Limit: 16000/8000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)

Total Submission(s): 1127 Accepted Submission(s): 369



Problem Description

Coco has a tree, whose nodes are conveniently labeled by 1,2,…,n, which has n-1 edge,each edge has a weight. An existing set S is initially empty.

Now there are two kinds of operation:

1 x: If the node x is not in the set S, add node x to the set S

2 x: If the node x is in the set S,delete node x from the set S

Now there is a annoying problem: In order to select a set of edges from tree after each operation which makes any two nodes in set S connected. What is the minimum of the sum of the selected edges’ weight ?



Input

one integer number T is described in the first line represents the group number of testcases.( T<=10 )

For each test:

The first line has 2 integer number n,q(0<n,q<=100000) describe the number of nodes and the number of operations.

The following n-1 lines each line has 3 integer number u,v,w describe that between node u and node v has an edge weight w.(1<=u,v<=n,1<=w<=100)

The following q lines each line has 2 integer number x,y describe one operation.(x=1 or 2,1<=y<=n)



Output

Each testcase outputs a line of "Case #x:" , x starts from 1.

The next q line represents the answer to each operation.



Sample Input

1
6 5
1 2 2
1 5 2
5 6 2
2 4 2
2 3 2
1 5
1 3
1 4
1 2
2 5




Sample Output

Case #1:
0
6
8
8
4




Author

FZUACM



Source

2015 Multi-University Training Contest 1



题意

给定一棵树以及q个询问。初始一个空的集合。两种询问,一种是往集合里添加一个点,一种是从集合里删除已经存在的点。对于每次询问,输出把集合里的点通过树的边连在一起所需要的最小代价(每条边都有权值)

思路

首先对这棵树预处理出DFS序。对集合的操作相当于构造了一棵新的树。

首先我们考虑插入操作。在已有的集合里寻找DFS序比操作点u大的最小点y以及小于操作点的最大点x。我们可以得到一个结论,联通这三点的路径是必须要加在新构造的树上的。(从x访问到y之间的路上不会出现集合上的点,加入u后,从x到u,u到y的路上也同样不会有集合上的点)所以我们只需要在现有的答案上加上u点到xy链的距离即可。这个距离就要通过求LCA来计算,公式

假设节点要连接到一个链中,链的定点(x,y),那么 u连接到x的距离是dfn[u] + dfn[x] - 2dfn[ lca(u,x) ] ;

u连接到y的距离dfn[u] + dfn[y] - 2dfn[ lca(u,x) ] :

x连接到y的距离dfn[x] + dfn[y] - 2dfn[ lca(x,y) ] :

u连接到x-y这个链的距离 = (u到y+u到x-x到y)/2

当找不到这样的两个点时,说明操作点的DFS序为最大或者最小。故我们只需要取出集合中DFS序最大和最小的两个点,同上来求即可。

删除时同理。注意在集合操作时,若添加则在添加前查找,删除则在删除后查找。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<time.h>
#include<string>
#define cl(a,b)	memset(a,b,sizeof(a))
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define REP(i,n) for(int i=0;i<n;++i)
#define REP1(i,a,b) for(int i=a;i<=b;++i)
#define REP2(i,a,b) for(int i=a;i>=b;--i)
#define MP make_pair
#define LL long long
#define ULL unsigned long long
#define X first
#define Y second
#define MAXN 100050
using namespace std;
vector<int> e[MAXN], c[MAXN];
set<int> st;
int fa[MAXN][20];
int p[MAXN];
int dfn[MAXN];
int dis[MAXN];
int cid;
int d[MAXN];
void dfs(int cur, int f) {
	dfn[++cid] = cur;
	p[cur] = cid;
	for (int i = 1; i < 20; ++i)
		fa[cur][i] = fa[fa[cur][i - 1]][i - 1];

	for (int i = 0; i < e[cur].size(); ++i) {
		int u = e[cur][i];
		if (u == f)
			continue;
		d[u] = d[cur] + 1;
		dis[u] = dis[cur] + c[cur][i];
		fa[u][0] = cur;
		dfs(u, cur);
	}
}

int lca(int u, int v) {
	if (d[u] < d[v])
		swap(u, v);
	for (int i = 19; i >= 0; --i) {
		if (d[fa[u][i]] >= d[v])
			u = fa[u][i];
		if (u == v)
			return u;
	}
	for (int i = 19; i >= 0; --i) {
		if (fa[u][i] != fa[v][i]) {
			u = fa[u][i];
			v = fa[v][i];
		}
	}
	return fa[u][0];
}
int add(int u) {
	if (st.empty())
		return 0;
	int x, y;
	set<int>::iterator it = st.lower_bound(p[u]), itx = it;
	itx--;
	if (it == st.end() || it == st.begin()) {
		it = st.begin();
		itx = st.end();
		itx--;
	}
	y = (*it);
	x = (*itx);
	y = dfn[y];
	x = dfn[x];
	return dis[u] - dis[lca(x, u)] - dis[lca(y, u)] + dis[lca(x, y)];
}
bool bo[MAXN];
int main() {
	//freopen("data.in", "r", stdin);
	//freopen("data.out", "w", stdout);
	int tt, ri = 0;
	scanf("%d", &tt);
	while (tt--) {
		int n, q;
		st.clear();
		cid=0;
		scanf("%d%d", &n, &q);
		for (int i = 0; i <= n; ++i)
		{
			bo[i] = false;
			e[i].clear();
			c[i].clear();
		}
		for (int i = 1; i < n; ++i) {
			int x, y, z;
			scanf("%d%d%d", &x, &y, &z);
			e[x].push_back(y);
			c[x].push_back(z);
			e[y].push_back(x);
			c[y].push_back(z);
		}
		for (int i = 0; i < 20; ++i)
			fa[1][0] = 1;
		d[1] = 1;
		dis[1] = 0;
		dfs(1, -1);
		int sum = 0;
		printf("Case #%d:\n", ++ri);
		for (int i = 1; i <= q; ++i) {
			int x, y;
			scanf("%d%d", &x, &y);
			int tmp;
			if (x == 1) {
				if (!bo[y]){
					bo[y]=true;
					if (st.size() == 0) {
						st.insert(p[y]);
					} else {
						tmp = add(y);
						st.insert(p[y]);
						sum += tmp;
					}
				}
			} else {
				if (bo[y]){
					bo[y] = false;
					st.erase(p[y]);
					if (!st.empty()) {
						sum -= add(y);

					}
				}
			}

			printf("%d\n", sum);
//		printf("%d %d %d %d\n",now,i,sum,add(i));
		}
	}
	return 0;
}


代码中LCA使用倍增算法。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <vector>
#include <set>
#include <map>
using namespace std;
int n,q;
const int MAXN = 100005;
int head[MAXN],cnt;
int dir[MAXN],p[MAXN][20],dep[MAXN],tme[MAXN],t;
int dfn[MAXN];
struct edge{
    int u,v,w,next;
    edge(){}
    edge(int u,int v,int w):u(u),v(v),w(w){}
}e[MAXN<<1];
struct point{
    int id;
    point(){};
    point(int id):id(id){}
    bool operator < (const point & b) const{
        return dfn[id] < dfn[b.id];
    }
};
set<point> se;
void addedge(int u,int v,int w){
    e[cnt] = edge(u,v,w),e[cnt].next = head[u];
    head[u] = cnt++;
    e[cnt] = edge(v,u,w),e[cnt].next = head[v];
    head[v] = cnt++;
}
void dfs(int u){
    tme[t++] = u;
    for(int i = head[u];i!=-1;i = e[i].next){
        int v = e[i].v;
        if(!dep[v]){
            dir[v] = dir[u] + e[i].w;
            dep[v] = dep[u]+1;
            p[v][0] = u;
            dfs(v);
        }
    }
}
void makep(int st){
    dfs(st);
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i<=n;i++)
            if(p[i][j-1]!=-1)
                p[i][j] = p[p[i][j-1]][j-1];
}
int LCA(int a,int b){
    if(dep[a] < dep[b])swap(a,b);
    int i;
    for(i = 0; (1<<i)<=dep[a];i++);
    i--;
    for(int j=i;j>=0;j--)
        if(dep[a]-(1<<j)>=dep[b])
            a = p[a][j];
    if(a == b)return a;
    for(int j = i;j >= 0;j--){
        if(p[a][j] != -1 && p[a][j] != p[b][j]){
            a = p[a][j];
            b = p[b][j];
        }
    }
    return p[a][0];
}
int main(){
    int T;
    cin>>T;
    for(int cas = 1;cas <= T;cas++){
        se.clear();
        printf("Case #%d:\n",cas);
        memset(head,-1,sizeof(head));
        memset(p,-1,sizeof(p));
        memset(dep,0,sizeof(dep));
        memset(dir,0,sizeof(dir));
        cnt = t = 0;
        scanf("%d%d",&n,&q);
        int u,v,w;
        dep[1] = 1;
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&u,&v,&w);
            addedge(u, v, w);
        }
        makep(1);
        for(int i=0;i<t;i++)dfn[tme[i]] = i;
        //for(int i=0;i<t;i++)printf("%d ",tme[i]);
        int st = 0;
        while(q--){
            int op,num;
            scanf("%d%d",&op,&num);
            if(op == 1){
                if(se.empty()){
                    puts("0");
                    se.insert(point(num));
                    continue;
                }
                if(se.find(point(num))!=se.end()){
                    printf("%d\n",st);
                    continue;
                }
                set<point>::iterator it = se.lower_bound(point(num));
                if(it == se.begin() || it == se.end()){
                        int x = (se.begin())->id,y = (--se.end())->id;
                        st += (dir[num] - dir[LCA(x,num)]-dir[LCA(y,num)] + dir[LCA(x,y)]);
                }
                else{
                    int x = (it--)->id, y = (it)->id;
                    st += (dir[num] - dir[LCA(x,num)]-dir[LCA(y,num)] + dir[LCA(x,y)]);
                }
                se.insert(point(num);
                printf("%d\n",st);
            }else{
                if(se.find(point(num))==se.end()){
                    printf("%d\n",st);
                    continue;
                }
                se.erase(point(num));
                if(se.empty()){
                    puts("0");
                    se.insert(point(num));
                    continue;
                }
                set<point>::iterator it = se.lower_bound(point(num));
                if(it == se.begin() || it == se.end()){
                        int x = (se.begin())->id,y = (--se.end())->id;
                        st -= (dir[num] - dir[LCA(x,num)]-dir[LCA(y,num)] + dir[LCA(x,y)]);
                }
                else{
                    int x = (it--)->id, y = (it)->id;
                    st -= (dir[num] - dir[LCA(x,num)]-dir[LCA(y,num)] + dir[LCA(x,y)]);
                }
                printf("%d\n",st);
            }
        }
    }
    return 0;
}
用树状数组维护
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
using namespace std;
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x < 0) {
		putchar('-');
		x = -x;
	}
	if (x > 9) pt(x / 10);
	putchar(x % 10 + '0');
}
typedef long long ll;
typedef pair<int, int> pii;
const int N = 100000 + 100;
struct Edge {
	int from, to, dis, nex;
}edge[N << 1];
int head
, edgenum;
void add(int u, int v, int d) {
	Edge E = { u,v,d, head[u] };
	edge[edgenum] = E;
	head[u] = edgenum++;
}
int dis
, fa
[20], dep
;
void bfs(int root) {
	queue<int> q;
	fa[root][0] = root;dep[root] = 0;dis[root] = 0;
	q.push(root);
	while (!q.empty()) {
		int u = q.front();q.pop();
		for (int i = 1;i<20;i++)fa[u][i] = fa[fa[u][i - 1]][i - 1];
		for (int i = head[u]; ~i;i = edge[i].nex) {
			int v = edge[i].to;if (v == fa[u][0])continue;
			dep[v] = dep[u] + 1;dis[v] = dis[u] + edge[i].dis;fa[v][0] = u;
			q.push(v);
		}
	}
}
int Lca(int x, int y) {
	if (dep[x]<dep[y])swap(x, y);
	for (int i = 0;i<20;i++)if ((dep[x] - dep[y])&(1 << i))x = fa[x][i];
	if (x == y)return x;
	for (int i = 19;i >= 0;i--)if (fa[x][i] != fa[y][i])x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}

int n, q;
int c
;
set<int>s;
inline int Lowbit(int x) { return x&(-x); }
void change(int i, int x)//i点增量为x
{
	while (i <= n)
	{
		c[i] += x;
		i += Lowbit(i);
	}
}
int sum(int x) {//区间求和 [1,x]
	int ans = 0;
	for (int i = x; i >= 1; i -= Lowbit(i))
		ans += c[i];
	return ans;
}
int p
, fp
, top;
void dfs(int u, int fa) {
	p[u] = ++top;
	fp[top] = u;
	for (int i(head[u]); ~i; i = edge[i].nex) {
		int v = edge[i].to; if (v == fa)continue;
		dfs(v, u);
	}
}
int Low(int l, int r) {
	int ans = -1;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (sum(mid) - sum(l-1))r = mid - 1, ans = mid;
		else l = mid + 1;
	}
	return ans;
}
int Up(int l, int r) {
	int ans = -1;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (sum(r) - sum(mid-1))l = mid + 1, ans = mid;
		else r = mid - 1;
	}
	return ans;
}
int main() {
	int T, Cas = 1; rd(T);
	while (T--) {
		memset(c, 0, sizeof c);
		memset(head, -1, sizeof head); edgenum = 0;
		rd(n); rd(q);
		for (int i = 1, u, v, w; i < n; i++) {
			rd(u); rd(v); rd(w);
			add(u, v, w); add(v, u, w);
		}
		bfs(1);
		top = 0;
		dfs(1, 1);
		printf("Case #%d:\n", Cas++);
		s.clear();
		int ans = 0;
		while (q--) {
			int op; int u; rd(op); rd(u);
			if (op == 1) {
				if (!s.count(u))
				{
					s.insert(u);
					if (s.size() > 1) 
					{
						int x = Up(1, p[u]), y = Low(p[u], n);
						if (x == -1 || y == -1)
						{
							x = Low(1, n); y = Up(1, n);
						}
						x = fp[x]; y = fp[y];
						ans += dis[u] - dis[Lca(x, u)] - dis[Lca(y, u)] + dis[Lca(x, y)];
					}
					change(p[u], 1);
				}
			}
			else {
				if (s.count(u)) 
				{
					s.erase(u);
					change(p[u], -1);
					if (s.size())
					{
						int x = Up(1, p[u]), y = Low(p[u], n);
						if (x == -1 || y == -1)
						{
							x = Low(1, n); y = Up(1, n);
						}
						x = fp[x]; y = fp[y];
						ans -= dis[u] - dis[Lca(x, u)] - dis[Lca(y, u)] + dis[Lca(x, y)];
					}
				}
			}
			pt(ans); puts("");
		}
	}
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: