2019百度之星初赛第四场1005:wls 的树

题目链接: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了四发。所以还是需要细心。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值