传送门:
http://acm.hdu.edu.cn/showproblem.php?pid=5296
题目大意:给出一棵树,每个边都有一个权值,现在有一个空的集合,两种操作,1 x吧x节点放到集合中(如果还没放入),2 x把x节点从集合中拿出来(已放入)。现在要求将集合中的点之间的边权之和
dfn[u] - dfn[ lca(x,u) ] - dfn[ lca(y,u) ] + dfn[ lca(x,y) ]
先说这个公式怎么用,首先dfs一个顺序,加一个节点u,如果u节点的dfs序,在集合中节点的dfs序之间,那么找到最接近的(u的dfs序)的两个数为x和y;如果u节点的dfs序在集合中节点的dfs序的一侧,那么x和y为集合中dfs序的最大值和最小值,,,,,这样带入公式中求的就是添加这个节点所带来的需要添加的距离,删除一个节点和添加时一样的。
假设节点要连接到一个链中,链的定点(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
画个图想一下就知道了,任意取一个点代表u连接链上的那个点
为什么这样取点呢,给定固定点把它们连通得到的树一定是固定的。这样选点的目的是为了不让边重复。
更有树链剖分在树上建立线段树哦!
用线段树代表集合还不如直接用set快,哎,何必呢,下回大struct板直接套!
set版本:
#include <cstdio>
#include <cstring>
#include <set>
#include <algorithm>
using namespace std ;
#define maxn 100050
struct E{
int v , w ;
int next ;
}edge[maxn<<1];
int head[maxn] , cnt ;
int rmq[maxn][20] ;
int dep[maxn] , p[maxn] , belong[maxn] , cid ;
int vis[maxn] , dfn[maxn] ;
set<int> s ;
set<int>::iterator iter ;
void add(int u,int v,int w) {
edge[cnt].v = v ; edge[cnt].w = w ;
edge[cnt].next = head[u] ; head[u] = cnt++ ;
edge[cnt].v = u ; edge[cnt].w = w ;
edge[cnt].next = head[v] ; head[v] = cnt++ ;
}
void dfs(int fa,int u) {
int i , j , v ;
p[u] = ++cid ;
belong[cid] = u ;
for(i = head[u] ; i != -1 ; i = edge[i].next ) {
v = edge[i].v ;
if( v == fa ) continue ;
dfn[v] = dfn[u] + edge[i].w ;
rmq[v][0] = u ;
for(j = 1 ; j < 19 ; j++)
rmq[v][j] = rmq[ rmq[v][j-1] ][j-1] ;
dep[v] = dep[u] + 1 ;
dfs(u,v) ;
}
}
int lca(int u,int v) {
if( dep[u] < dep[v] ) swap(u,v) ;
int i ;
for(i = 19 ; i >= 0 ; i--) {
if( dep[ rmq[u][i] ] >= dep[v] )
u = rmq[u][i] ;
if( u == v ) return u ;
}
for(i = 19 ; i >= 0 ; i--) {
if( rmq[u][i] != rmq[v][i] ) {
u = rmq[u][i] ;
v = rmq[v][i] ;
}
}
return rmq[u][0] ;
}
int solve(int u) {
if( s.empty() ) return 0 ;
int x , y ;
iter = s.upper_bound(u) ;
if( iter == s.end() || iter == s.begin() ) {
x = belong[ *s.begin() ] ;
y = belong[ *s.rbegin() ] ;
}
else {
x = belong[*iter] ;
iter-- ;
y = belong[*iter] ;
}
u = belong[u] ;
return dfn[u] - dfn[ lca(x,u) ] - dfn[ lca(y,u) ] + dfn[ lca(x,y) ] ;
}
int main() {
int Step = 0 , t ;
int n , m ;
int i , j , u , v , w , k ;
int ans ;
scanf("%d", &t) ;
while( t-- ) {
memset(head,-1,sizeof(head)) ;
memset(rmq,0,sizeof(rmq)) ;
memset(dfn,0,sizeof(dfn)) ;
memset(vis,0,sizeof(vis)) ;
cnt = cid = ans = 0 ;
s.clear() ;
scanf("%d %d", &n, &m) ;
for(i = 1 ; i < n ; i++) {
scanf("%d %d %d", &u, &v, &w) ;
add(u,v,w) ;
}
dep[1] = 1 ;
dfs(-1,1) ;
printf("Case #%d:\n", ++Step) ;
while( m-- ) {
scanf("%d %d", &k, &u) ;
u = p[u] ;
if( k == 1 ) {
if( !vis[u] ) {
vis[u] = 1 ;
ans += solve(u) ;
s.insert(u) ;
}
}
else {
if( vis[u] ) {
vis[u] = 0 ;
s.erase(u) ;
ans -= solve(u) ;
}
}
printf("%d\n", ans) ;
}
}
return 0 ;
}
线段树版本:
//#pragma comment(linker, "/STACK:1677721600")
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf (-((LL)1<<40))
#define lson k<<1, L, (L + R)>>1
#define rson k<<1|1, ((L + R)>>1) + 1, R
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a, b) memset(a, b, sizeof(a))
#define FIN freopen("in.txt", "r", stdin)
#define FOUT freopen("out.txt", "w", stdout)
#define rep(i, a, b) for(int i = (a); i <= (b); i ++)
#define dec(i, a, b) for(int i = (a); i >= (b); i --)
template<class T> T MAX(T a, T b) { return a > b ? a : b; }
template<class T> T MIN(T a, T b) { return a < b ? a : b; }
template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b; }
//typedef __int64 LL;
typedef long long LL;
const int MAXN = 100000 + 100;
const int MAXM = 2000000 + 100;
const double eps = 1e-8;
LL MOD = 1000000007;
struct Edge {
int v, w;
Edge(int _v = 0, int _w = 0) {
v = _v; w = _w;
}
};
struct LCA {
int idx[MAXN << 1];
int dep[MAXN << 1];
int dp[MAXN << 1][20];
int K[MAXN << 1];
int node_cnt;
vector<Edge>G[MAXN];
int P[MAXN];
int dis[MAXN];
void init(int n) {
mem0(dep); mem0(K);
node_cnt = 0;
rep (i, 0, n) G[i].clear();
}
void add_edge(int u, int v, int w) {
G[u].push_back(Edge(v, w));
G[v].push_back(Edge(u, w));
}
void dfs(int u, int fa, int height, int dist) {
idx[++node_cnt] = u;
dep[node_cnt] = height;
P[u] = node_cnt;
dis[u] = dist;
int sz = G[u].size();
rep (i, 0, sz - 1) {
int v = G[u][i].v;
if(v == fa) continue;
dfs(v, u, height + 1, dist + G[u][i].w);
idx[++node_cnt] = u;
dep[node_cnt] = height;
}
}
void init_st_table() {
dfs(1, -1, 0, 0);
int n = node_cnt;
rep (i, 1, n) {
dp[i][0] = i;
while((1 << (K[i] + 1)) <= i) K[i] ++;
}
for(int j = 1; (1 << j) <= n; j ++) {
for(int i = 1; i + (1 << j) - 1 <= n; i ++) {
int l_pos = dp[i][j - 1], r_pos = dp[i + (1 << (j - 1))][j - 1];
dp[i][j] = dep[l_pos] < dep[r_pos] ? l_pos : r_pos;
}
}
}
int rmq_query(int L, int R) {
if(L > R) swap(L, R);
int len = R - L + 1, k = K[len];
return dep[dp[L][k]] < dep[dp[R - (1 << k) + 1][k]] ? dp[L][k] : dp[R - (1 << k) + 1][k];
}
int lca_query(int u, int v) {
int id = rmq_query(P[u], P[v]);
return idx[id];
}
}lca;
struct SegTree {
int s[MAXN << 3];
void update(int k, int L, int R, int p, int v) {
if(L == R) { s[k] = v; return ; }
if(((L + R) >> 1) >= p) update(lson, p, v);
else update(rson, p, v);
s[k] = s[k << 1] + s[k << 1 | 1];
}
int query_sum(int k, int L, int R, int p) {
if(R <= p) return s[k];
if( ((L + R) >> 1) >= p ) return query_sum(lson, p);
return s[k << 1] + query_sum(rson, p);
}
int query_pos(int k, int L, int R, int x) {
if(L == R) return L;
if(s[k << 1] >= x) return query_pos(lson, x);
return query_pos(rson, x - s[k << 1]);
}
}st;
int t, n, m, cas = 0;
int u, v, w;
bool vis[MAXN << 1];
int main()
{
// FIN;
cin >> t;
while(t--) {
scanf("%d %d", &n, &m);
lca.init(n);
rep (i, 2, n) {
scanf("%d %d %d", &u, &v, &w);
lca.add_edge(u, v, w);
}
lca.init_st_table();
mem0(st.s);
mem0(vis);
int ans = 0;
printf("Case #%d:\n", ++cas);
while(m --) {
scanf("%d %d", &u, &v);
if( (u == 1 && !vis[v]) || (u == 2 && vis[v]) ) {
vis[v] = !vis[v];
if(u == 2) st.update(1, 1, lca.node_cnt, lca.P[v], 0);
if( st.s[1] ) {
int x, y;
int sum = st.query_sum(1, 1, lca.node_cnt, lca.P[v]);
if( !sum || sum == st.s[1] ) x = 1, y = st.s[1];
else x = sum, y = sum + 1;
x = lca.idx[st.query_pos(1, 1, lca.node_cnt, x)];
y = lca.idx[st.query_pos(1, 1, lca.node_cnt, y)];
int xv = lca.lca_query(x, v);
int yv = lca.lca_query(y, v);
int xy = lca.lca_query(x, y);
ans += (u == 1 ? 1 : -1) * (lca.dis[v] - lca.dis[xv] - lca.dis[yv] + lca.dis[xy]);
}
else ans = 0;
if(u == 1) st.update(1, 1, lca.node_cnt, lca.P[v], 1);
}
printf("%d\n", ans);
}
}
return 0;
}
这个就更有意思了,都用set了,为何不用upperbound查找,偏偏要用一下树状数组呢,真是醉了。。。。
#pragma comment(linker, "/STACK:1024000000,1024000000")
#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[N], edgenum;
void add(int u, int v, int d) {
Edge E = { u,v,d, head[u] };
edge[edgenum] = E;
head[u] = edgenum++;
}
int dis[N], fa[N][20], dep[N];
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[N];
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[N], fp[N], 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;
}
倍增lca版:
#include "iostream"
#include "cstring"
#include "cstdio"
#include "vector"
#define PB push_back
#define MP make_pair
#define F first
#define S second
using namespace std;
const int N = 100010;
vector<pair<int, int> > e[N];
int weight[N];
int t[N << 2];
int f[N][21];
int pos[N], lpos[N], rpos[N], level[N];
int total;
void build(int l, int r, int idx)
{
t[idx] = 0;
if(l == r) return;
int m = (l + r) / 2;
build(l, m, idx << 1);
build(m + 1, r, idx << 1 | 1);
}
void dfs(int x, int pa, int dep)
{
level[x] = dep;
int now = f[x][0] = pa;
for(int i = 0; now != -1 && f[now][i] != -1; ++ i){
f[x][i + 1] = f[now][i];
now = f[now][i];
}
lpos[x] = ++total;
pos[total] = x;
for(int i = 0; i < e[x].size(); ++ i){
int u = e[x][i].F;
int w = e[x][i].S;
if(u == pa) continue;
weight[u] = weight[x] + w;
dfs(u, x, dep + 1);
}
rpos[x] = total;
}
void update(int l, int r, int id, int v, int idx)
{
if(l == r){
t[idx] += v;
return;
}
int m = (l + r) / 2;
if(m < id){
update(m + 1, r, id, v, idx << 1 | 1);
}else{
update(l, m, id, v, idx << 1);
}
t[idx] = t[idx << 1] + t[idx << 1 | 1];
}
int query(int l, int r, int L, int R, int idx)
{
if(l >= L && r <= R) return t[idx];
int m = (l + r) / 2;
if(m < L) return query(m + 1, r, L, R, idx << 1 | 1);
else if(m >= R) return query(l, m, L, R, idx << 1);
else return query(l, m, L, m, idx << 1) + query(m + 1, r, m + 1, R, idx << 1 | 1);
}
int jump(int x, int step)
{
int k = 0;
while(step){
if(step & 1) x = f[x][k];
step >>= 1;
k ++;
}
return x;
}
int n;
int findlca(int x, int num)
{
int l = 0, r = level[x];
while(l < r){
int m = (l + r) / 2;
int u = jump(x, m);
if(query(1, n, lpos[u], rpos[u], 1) >= num){
r = m;
}else{
l = m + 1;
}
}
return jump(x, l);
}
int find_element(int x)
{
int l = lpos[x], r = rpos[x];
while(l < r){
int m = (l + r) / 2;
int u = query(1, n, l, m, 1);
if(u >= 1){
r = m;
}else{
l = m + 1;
}
}
return pos[l];
}
int getnum(int x)
{
return query(1, n, lpos[x], rpos[x], 1);
}
int vis[N];
int main(void)
{
int num_tests, g = 0;
scanf("%d", &num_tests);
while(num_tests --){
total = 0;
printf("Case #%d:\n", ++ g);
int q, x, y, w;
scanf("%d %d", &n, &q);
for(int i = 1; i <= n; ++ i){
e[i].clear();
}
for(int i = 1; i < n; ++ i){
scanf("%d %d %d",&x, &y, &w);
e[x].PB(MP(y, w));
e[y].PB(MP(x, w));
}
build(1, n, 1);
memset(f, -1, sizeof(f));
memset(vis, 0, sizeof(vis));
weight[1] = 0;
dfs(1, -1, 0);
int num = 0;
int ans = 0;
for(int i = 1; i <= q; ++ i){
scanf("%d %d", &x, &y);
if(x == 1){
if(vis[y]){
cout << ans << '\n';
continue;
}
vis[y] = 1;
num ++;
if(num <= 1){
ans = 0;
}else{
int u = findlca(y, 1);
int v = find_element(u);
if(getnum(u) == num - 1){
if(getnum(u) == getnum(v)){
ans += weight[y] - weight[u];
ans += weight[v] - weight[u];
}else{
int t = findlca(v, num - 1);
if(t == u){
ans += weight[y] - weight[u];
}else{
ans += weight[y] - weight[u];
ans += weight[t] - weight[u];
}
}
}else{
ans += weight[y] - weight[u];
}
}
update(1, n, lpos[y], 1, 1);
}else{
if(!vis[y]){
cout << ans << '\n';
continue;
}
vis[y] = 0;
num --;
update(1, n, lpos[y], -1, 1);
if(num <= 1){
ans = 0;
}else{
int u = findlca(y, 1);
int v = find_element(u);
if(getnum(u) == num){
int target = findlca(v, num);
ans -= weight[y] - weight[u];
ans -= weight[target] - weight[u];
}else{
ans -= weight[y] - weight[u];
}
}
}
cout << ans << '\n';
}
}
return 0;
}