题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6723
题目截图:
解题思路:首先题目有两个操作,第一个是修改操作,第二个是查询操作。一般对于修改的题,我们可以假修改,用一个数据结构来保存修改了那些值就好了。比如这一题我们可以先建立n个set,我刚刚开始的想法。然后set之间启发式合并,查询就判断两个人的并查集是否一样。不一样的话就是在两个set中,那么答案就是这两个点在set中比这两个点大的个数,然后再求fx和fy的LCA求距离就是答案了。如果在同一个set里面的话就是两个的分别比自己大的个数相减的绝对值。但是使用set的话就不能查询比这个树大的有几个了。所以我们可以使用到树的性质,每个点的编号都不一样。那么就可以使用权值线段树来替代这个set,刚刚开始可以开n棵权值线段树,每棵权值线段树的大小都是logn,刚刚开始。然后可以合并。
代码如下:
#include<bits/stdc++.h>
#define FRD freopen("in.txt", "r", stdin)
#define __ctz(x) __builtin_ctz(x) //返回二进制下末尾有几个零
#define __ffs(x) __builtin_ffs(x) //返回二进制下最后一个1的位置,从1开始
#define ms(x) memset(x,0,sizeof(x))
#define pb push_back
#define mk make_pair
#define all(x) x.begin(),x.end()
#define lson (id<<1)
#define rson (id<<1|1)
#define fi first
#define se second
using namespace std;
typedef long long int LL;
typedef pair<LL,LL> pll;
const LL INF = 1e18;
const int N = 1e5+7;
inline int read() {char ch=getchar();int x=0,f=1;while(ch<'0'||ch>'9') {if(ch == '-') f = -1;ch = getchar();}while('0'<=ch&&ch<='9') {x=x*10+ch-'0';ch=getchar();}return x*f;}
inline LL readl() {char ch=getchar();LL x=0,f=1;while(ch<'0'||ch>'9') {if(ch == '-') f = -1;ch = getchar();}while('0'<=ch&&ch<='9') {x=x*10+ch-'0';ch=getchar();}return x*f;}
int rt[N],pre[N],tot[N*40],ls[N*40],rs[N*40];
int n,q,cnt;
vector<int> G[N];
int depht[N], father[N][22], lg[N];
inline void dfs(int now,int fath, vector<int> G[]) {
depht[now]=depht[fath]+1;
father[now][0]=fath;
for(register int i=1;(1<<i)<=depht[now];++i)
father[now][i]=father[father[now][i-1]][i-1];
for(register int v:G[now]) if(v!=fath) dfs(v,now,G);
}
inline int LCA(int x,int y) {
if(depht[x]<depht[y])
swap(x,y);
while(depht[x]>depht[y])
x=father[x][lg[depht[x]-depht[y]]-1];
if(x==y) return x;
for(register int k=lg[depht[x]];k>=0;--k)
if(father[x][k]!=father[y][k])
x=father[x][k],y=father[y][k];
return father[x][0];
}
inline void make_lg_table(int n) {
for(register int i=1;i<=n;++i)
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
}
void push_up(int id) {
tot[id] = tot[ls[id]]+tot[rs[id]];
}
void build_tree(int &k, int l, int r, int x) {
if (k == 0) k = ++cnt;
if (l == r) {
tot[k] = 1;
return ;
}
int mid = (l+r)/2;
if (x <= mid) {
build_tree(ls[k],l,mid,x);
} else {
build_tree(rs[k],mid+1,r,x);
}
push_up(k);
}
int find(int x) {
return pre[x] = pre[x]==x?x:find(pre[x]);
}
int merge(int x, int y) {
if (x==0 || y == 0) return x+y;
ls[x] = merge(ls[x],ls[y]);
rs[x] = merge(rs[x],rs[y]);
tot[x] += tot[y];
return x;
}
void solve_1(int u, int fa, int f) {
pre[u] = f;
for (int v : G[u]) {
if (v == fa) continue;
if (pre[v] == v) {
solve_1(v,u,f);
rt[u] = merge(rt[u],rt[v]);
}
}
}
int query(int k, int l, int r, int x) {
if (l == r) {
return 1;
}
int mid = (l+r)/2;
if (x <= mid) {
return tot[rs[k]]+query(ls[k],l,mid,x);
} else {
return query(rs[k],mid+1,r,x);
}
}
int solve_2(int x, int y) {
int fx = find(x), fy = find(y);
if (fx == fy) {
return abs(query(rt[fx],1,n,x)-query(rt[fy],1,n,y));
} else {
return query(rt[fx],1,n,x)+query(rt[fy],1,n,y)-2*depht[LCA(fx,fy)]+depht[fx]+depht[fy]-2;
}
}
int main() {
#ifndef ONLINE_JUDGE
int startTime = clock();
FRD;
#endif
make_lg_table(N-7);
int T = read();
for (int Case = 1; Case <= T; Case++) {
n = read();
cnt = 0;
ms(father); ms(depht);
ms(rt); ms(ls); ms(rs); ms(tot);
for (int i = 1; i <= n; i++) {
G[i].clear();
pre[i] = i;
}
for (int i = 1; i < n; i++) {
int u = read(), v = read();
G[u].pb(v); G[v].pb(u);
}
for (int i = 1; i <= n; i++) {
build_tree(rt[i],1,n,i);
}
dfs(1,1,G);
q = read();
for (int qs = 1; qs <= q; qs++) {
int op = read();
if (op == 1) {
int x = read();
if (pre[x] == x) {
solve_1(x,father[x][0],x);
}
} else {
int x = read(), y = read();
printf("%d\n", solve_2(x,y));
}
}
}
#ifndef ONLINE_JUDGE
printf("Time = %dms\n", clock() - startTime);
#endif
return 0;
}
代码注意地方:刚刚开始把G[v].pb(u)里面的u写成了v,导致TLE了四发。所以还是需要细心。