您的位置:首页 > 其它

Gym 100962F Frank Sinatra

2016-05-12 13:18 295 查看
树上莫队。

关于树上莫队,需要做的是把树分块。可以直接按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
; 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
; 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  树上莫队