更改点后输出把集合里的点通过树的边连在一起所需要的最小代价 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; }
相关文章推荐
- SPOJ VLATTICE Visible Lattice Points (莫比乌斯反演基础题)
- 用 Apache 和 Subversion 搭建安全的版本控制环境
- SDUTOJ 3131 A回(完全背包)
- Mysql实现full join的替换方法
- java必看书籍
- Android:TabHost实现Tab切换
- Android获得屏幕分辨率的两种方法
- Android基础类之BaseAdapter
- STL学习----入门(1)[map]
- sql over函数使用
- 深圳市居住证查询、办理政府网址
- UVALIVE 3713 Astronauts(2-SAT)
- yourphp内置编辑器
- NoSql-MongoDB+Dos窗口下的增删改
- ucosII移植:可重入代码
- linux vnc服务配置详细教程
- MS sql 小技巧
- C# interface 接口理解
- Poco C++——JSON解析
- 解决Ubuntu系统中文乱码显示问题