Gym 100962F Frank Sinatra

树上莫队。

关于树上莫队,需要做的是把树分块。可以直接按dfs序分块,但是这样速度比较慢。还有一种就是按后序遍历的方法去分块。这样就可以按左端点的块号排序,再按右端点的dfs序排序,然后上莫队了。

还需要注意的是转移。需要的是每次把链给变掉,这个可以用两端点的变化来搞定。链的异或操作,可以实现链的转换。


这道题题意是,给一棵树,然后每条边有边权。给你q个查询,每个查询问你一条链上最小的没出现的数字是多少。于是可以莫队+分块来做。由于是单组数据,所以随便跑。但是这道题好像卡常数了,然后好像dfs还会爆栈(后来发现并没有哭)。。于是我手写栈的时候忘了记录dfn,tle到死,各种小优化到最后才发现这个问题。

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#define xx first
#define yy second
#define LL long long
#define MP make_pair
#define inf 0x3f3f3f3f
#define CLR(a, b) memset(a, b, sizeof(a))

#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

using namespace std;

const int maxn = 100100;

int fa[maxn][22], dep[maxn], dfn[maxn], belong[maxn];
int val[maxn], cnt[maxn * 3], block[maxn];
int K, idx;

vector<pair<int, int> > G[maxn];

struct Q {
    int a, b, id;
    bool operator < (const Q& rhs) const {
        return (belong[a] == belong[rhs.a] && dfn[b] < dfn[rhs.b])
               || belong[a] < belong[rhs.a];
    }
} qrt[maxn];

//struct Q {
//    int a, b, id;
//    bool operator < (const Q& rhs) const {
//        return (dfn[a] / K == dfn[rhs.a] / K && dfn[b] < dfn[rhs.b])
//               || dfn[a] / K < dfn[rhs.a] / K;
//    }
//} qrt[maxn];

struct Node {
    int u, p, i;
    Node() {}
    Node(int u, int p, int i)
        : u(u), p(p), i(i) {}
};

int stk[maxn];
int fir[maxn], nxt[maxn * 2], to[maxn * 2], dis[maxn * 2], ecnt;


/**
void dfs(int u, int p) {
	dfn[u] = idx ++;
	fa[u][0] = p; dep[u] = dep[p] + 1;
	for(int i = 0; i < (int)G[u].size(); i ++) {
		int v = G[u][i].xx, c = G[u][i].yy;
		if(v == p) continue;
		val[v] = c;
		dfs(v, u);
		if(sz >= K) {
            while(sz) {
                belong[stk[sz - 1]] = tot;
                sz --;
            }
            tot ++;
        }
	}
	stk[sz ++] = u;
}
*/

inline void dfs(int u, int p) {
    stack<Node> S;
    int sz = 0, tot = 0, idx = 0;
    S.push(Node(u, p, fir[u]));
    dfn[u] = idx ++;
    fa[u][0] = 0; dep[u] = 1;
    while(!S.empty()) {
        Node u = S.top(); S.pop();
        if(u.i == -1) {
            stk[sz ++] = u.u;
            if(sz >= K) {
                while(sz) {
                    belong[stk[sz - 1]] = tot;
                    sz --;
                }
                tot ++;
            }
            continue;
        }
        S.push(Node(u.u, u.p, nxt[u.i]));
        int v = to[u.i];
        if(v == u.p) continue;
        fa[v][0] = u.u;
        dep[v] = dep[u.u] + 1;
        val[v] = dis[u.i];
        dfn[v] = idx ++;
        S.push(Node(v, u.u, fir[v]));
    }
    while(sz) {
        belong[stk[sz - 1]] = tot;
        sz --;
    }
    tot ++;
}

int ans[maxn], vec[maxn * 3], Log[maxn];

inline void LCA_init(int n) {
    Log[0] = -1;
    for(int i = 1; i <= n; i ++) Log[i] = Log[i >> 1] + 1;
    for(int j = 1; j < 20; j ++) {
        for(int i = 1; i <= n; i ++)
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
    }
}

int n;

inline int LCA(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    int c = dep[v] - dep[u];
    for(int i = Log[c]; i >= 0; i --) if((1 << i) & c) {
            v = fa[v][i];
        }
    if(u == v) return u;
    for(int i = Log[n]; i >= 0; i --) {
        if(fa[u][i] == fa[v][i]) continue;
        u = fa[u][i]; v = fa[v][i];
    }
    return fa[u][0];
}

bool vis[maxn];

inline void gao(int u, int v, int lca) {
    while(u != lca) {
        if(vis[u]) {
            cnt[val[u]] --;
            if(cnt[val[u]] == 0) block[val[u] / K] --;
            vis[u] = false;
        } else {
            cnt[val[u]] ++;
            if(cnt[val[u]] == 1) block[val[u] / K] ++;
            vis[u] = true;
        }
        u = fa[u][0];
    }
    while(v != lca) {
        if(vis[v]) {
            cnt[val[v]] --;
            if(cnt[val[v]] == 0) block[val[v] / K] --;
            vis[v] = false;
        } else {
            cnt[val[v]] ++;
            if(cnt[val[v]] == 1) block[val[v] / K] ++;
            vis[v] = true;
        }
        v = fa[v][0];
    }
}

inline int mex() {
    if(cnt[0] == 0) return 0;
    for(int i = 0; ; i ++) {
        if(block[i] != K) {
            for(int j = i * K; ; j ++)
                if(cnt[j] == 0) return vec[j];
        }
    }
    return 0;
}

inline int getint() {
    char c = getchar();
    int con = 0;
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') con = con * 10 + c - '0', c = getchar();
    return con;
}

inline void add(int u, int v, int c) {
    to[ecnt] = v; dis[ecnt] = c;
    nxt[ecnt] = fir[u]; fir[u] = ecnt ++;
}

int main() {
    int q; ecnt = 0;
    scanf("%d%d", &n, &q); {
        K = sqrt(n + 0.0);
        CLR(fir, -1);
        int vsz = 0; vec[vsz ++] = 0;
        for(int i = 2; i <= n; i ++) {
            int u = getint(), v = getint(), c = getint();
            add(u, v, c); add(v, u, c);
            vec[vsz ++] = c; vec[vsz ++] = c + 1;
        }
        sort(vec, vec + vsz);
        vsz = unique(vec, vec + vsz) - vec;
        dep[0] = 0; CLR(fa, 0); idx = 0;
        dfs(1, 0); LCA_init(n);
        for(int i = 1; i <= n; i ++) {
            val[i] = lower_bound(vec, vec + vsz, val[i]) - vec;
        }
        for(int i = 0; i < q; i ++) {
            qrt[i].a = getint();
            qrt[i].b = getint();
            if(belong[qrt[i].a] > belong[qrt[i].b])
                swap(qrt[i].a, qrt[i].b);
//            if(dfn[qrt[i].a] > dfn[qrt[i].b])
//                swap(qrt[i].a, qrt[i].b);
            qrt[i].id = i;
        }
        sort(qrt, qrt + q);
        CLR(vis, false); CLR(cnt, 0); CLR(block, 0);
        gao(qrt[0].a, qrt[0].b, LCA(qrt[0].a, qrt[0].b));
        ans[qrt[0].id] = mex();
        for(int i = 1; i < q; i ++) {
            gao(qrt[i - 1].a, qrt[i].a, LCA(qrt[i - 1].a, qrt[i].a));
            gao(qrt[i - 1].b, qrt[i].b, LCA(qrt[i - 1].b, qrt[i].b));
            ans[qrt[i].id] = mex();
        }
        for(int i = 0; i < q; i ++) {
            printf("%d\n", ans[i]);
        }
    }
    return 0;
}


第二种姿势。

首先可以知道的是,对于一棵树的dfs序(访问u时记录一次,离开u时记录一次),我们可以用一个区间中出现奇数次的点来表示一个路径,具体类似这个。由于本题是用边下面的点表示该边,所以对于两个点的最近公共祖先是没必要记录的。所以就直接可以用区间表示两点之间的路径。其中如果两点[u, v]路径,其中u是v的祖先的话,我们需要把u点给去掉。具体可以通过路径的具体表现形式来实现。

鉴于大家可能不怎么会表示该路径,这里说一下路径的表示形式。对于两点之间的路径[u, v](这里我们默认先访问了u节点),那么u->v的路径就可以表示为离开u到到达v这个序列。比如对于样例:

7 6
2 1 1
3 1 2
1 4 0
4 5 1
5 6 3
5 7 4

则dfs序列为:

(1 4 5 7 7 6 6 5 4 3 3 2 2 1)

(注意由于用的是邻接表存的边,所以序列是这样)

那么对于[1, 7]就可以用序列(1 4 5 7)表示,对于本题需要把祖先节点去掉,于是表示为(4 5 7)

对于[2, 6],可以用序列(6 5 4 3 3 2)表示,其种3点出现了偶数次,需要排除掉,于是剩下的便是去掉公共祖先的路径。想象一下还是挺容易理解。

于是树上的询问就变成了一个序列区间了,且在该序列区间中出现奇数次的点是有效点,于是就可以直接莫队了。

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#define xx first
#define yy second
#define LL long long
#define MP make_pair
#define inf 0x3f3f3f3f
#define CLR(a, b) memset(a, b, sizeof(a))

#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

using namespace std;

const int maxn = 100100;

int fa[maxn][22], dep[maxn], dfn[maxn * 2], fst[maxn], sec[maxn];
int val[maxn], cnt[maxn * 3], block[maxn];
int K, idx;

vector<pair<int, int> > G[maxn];

struct Q {
    int a, b, id;
    bool operator < (const Q& rhs) const {
        return (a / K == rhs.a / K && b < rhs.b)
               || a / K < rhs.a / K;
    }
} qrt[maxn];

int fir[maxn], nxt[maxn * 2], to[maxn * 2], dis[maxn * 2], ecnt;
int ans[maxn], vec[maxn * 3], Log[maxn];
int n;

void dfs(int u, int p) {
    fst[u] = ++ idx;
    dfn[idx] = u;
    dep[u] = dep[p] + 1;
    fa[u][0] = p;
    for(int i = fir[u]; ~i; i = nxt[i]) {
        int v = to[i], c = dis[i];
        if(v == p) continue;
        val[v] = c;
        dfs(v, u);
    }
    sec[u] = ++ idx;
    dfn[idx] = u;
}

inline void LCA_init(int n) {
    Log[0] = -1;
    for(int i = 1; i <= n; i ++) Log[i] = Log[i >> 1] + 1;
    for(int j = 1; j < 20; j ++) {
        for(int i = 1; i <= n; i ++)
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
    }
}

inline int LCA(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    int c = dep[v] - dep[u];
    for(int i = Log[c]; i >= 0; i --) if((1 << i) & c) {
            v = fa[v][i];
        }
    if(u == v) return u;
    for(int i = Log[n]; i >= 0; i --) {
        if(fa[u][i] == fa[v][i]) continue;
        u = fa[u][i]; v = fa[v][i];
    }
    return fa[u][0];
}

int vis[maxn];

inline int mex() {
    if(cnt[0] == 0) return 0;
    for(int i = 0; ; i ++) {
        if(block[i] != K) {
            for(int j = i * K; ; j ++)
                if(cnt[j] == 0) return vec[j];
        }
    }
    return 0;
}

inline int getint() {
    char c = getchar();
    int con = 0;
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') con = con * 10 + c - '0', c = getchar();
    return con;
}

inline void add(int u, int v, int c) {
    to[ecnt] = v; dis[ecnt] = c;
    nxt[ecnt] = fir[u]; fir[u] = ecnt ++;
}

void add(int x) {
    cnt[x] ++;
    if(cnt[x] == 1) block[x / K] ++;
}

void sub(int x) {
    cnt[x] --;
    if(cnt[x] == 0) block[x / K] --;
}

int main() {
    int q; ecnt = 0;
    scanf("%d%d", &n, &q); {
        K = sqrt(n + 0.0);
        CLR(fir, -1);
        int vsz = 0; vec[vsz ++] = 0;
        for(int i = 2; i <= n; i ++) {
            int u = getint(), v = getint(), c = getint();
            add(u, v, c); add(v, u, c);
            vec[vsz ++] = c; vec[vsz ++] = c + 1;
        }
        sort(vec, vec + vsz);
        vsz = unique(vec, vec + vsz) - vec;
        dep[0] = 0; CLR(fa, 0); idx = 0;
        dfs(1, 0); LCA_init(n);
        for(int i = 1; i <= n; i ++) {
            val[i] = lower_bound(vec, vec + vsz, val[i]) - vec;
        }

        for(int i = 0; i < q; i ++) {
            qrt[i].a = getint();
            qrt[i].b = getint();
            int lca = LCA(qrt[i].a, qrt[i].b);
            if(lca == qrt[i].b)
                swap(qrt[i].a, qrt[i].b);
            if(lca == qrt[i].a) {
                qrt[i].a = fst[qrt[i].a] + 1;
                qrt[i].b = fst[qrt[i].b];
            }
            else {
                if(fst[qrt[i].a] > fst[qrt[i].b])
                    swap(qrt[i].a, qrt[i].b);
                qrt[i].a = sec[qrt[i].a];
                qrt[i].b = fst[qrt[i].b];
            }
            qrt[i].id = i;
        }
        sort(qrt, qrt + q);
        CLR(vis, 0); CLR(cnt, 0); CLR(block, 0);
        int L = 1, R = 0;
        for(int i = 0; i < q; i ++) {
            while(L > qrt[i].a) {
                L --;
                vis[dfn[L]] ++;
                if(vis[dfn[L]] == 1) add(val[dfn[L]]);
                else sub(val[dfn[L]]);
            }
            while(R < qrt[i].b) {
                R ++;
                vis[dfn[R]] ++;
                if(vis[dfn[R]] == 1) add(val[dfn[R]]);
                else sub(val[dfn[R]]);
            }
            while(L < qrt[i].a) {
                vis[dfn[L]] --;
                if(vis[dfn[L]] == 1) add(val[dfn[L]]);
                else sub(val[dfn[L]]);
                L ++;
            }
            while(R > qrt[i].b) {
                vis[dfn[R]] --;
                if(vis[dfn[R]] == 1) add(val[dfn[R]]);
                else sub(val[dfn[R]]);
                R --;
            }
            ans[qrt[i].id] = mex();
        }
        for(int i = 0; i < q; i ++) {
            printf("%d\n", ans[i]);
        }
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值