hdu 5296 Annoying problem

16 篇文章 0 订阅
10 篇文章 0 订阅

传送门:
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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值