【题意】
You are given a tree with N nodes.
The tree nodes are numbered from 1 to N.
Each node has an integer weight.
We will ask you to perfrom the following operation:
u v : ask for how many different integers that represent the weight of nodes there are on the path from u to v.
【分析】
首先离散化,把w转化一下,开始看到integer以为是-32768和32767间的整数,直接开桶存储,然后就悲剧地挂掉了...
然后DFS一遍,lst保存入和出的总序列,in保存第i个点入的编号,out保存出的编号
对于路径:树上倍增求LCA
对于问题:树上莫队。
(1)若x=y答案就是1
(2)若LCA(x,y)=x或y则左端点为min(in[x],in[y]),右端点为max(in[x],in[y])
(3)若都不同则左端点为max(out[i],out[j]),右为min(in[i],in[j]),这是可以证明的。
然后用莫队算法排序预处理,直接暴力,暴力时要用has[N]表示这个节点有没有被访问,开v[N]存储每个元素有多少个,然后访问一个节点has[N]就 xor 1。
注意要按照题目的顺序输出答案,开ans记录每个答案就是了
下面的代码有相关易错点...
【实现】
<span style="font-size:14px;">#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> using namespace std; const int N=40001; const int M=161121; const int K=16; struct R { int d,id; }rk[N]; int n,m,w[N]; struct G { int v,nxt; }map[N<<1]; int tot,hd[N]; int in[N],out[N],lst[N<<1],num; int pre[K+2][N],dep[N]; struct Q { int l,r,anc,id; }q[M]; int v[N],unit,res,l,r,has[N],ans[M]; //注意ans的数据范围,为询问的个数 inline void ins(int u,int v) { map[++tot].v=v; map[tot].nxt=hd[u]; hd[u]=tot; } void DFS(int now,int high) { dep[now]=high; in[now]=++num; lst[num]=now; for (int k=hd[now];k;k=map[k].nxt) if (!dep[map[k].v]) { DFS(map[k].v,high+1); pre[0][map[k].v]=now; } out[now]=++num; lst[num]=now; } inline int cmp1(R a,R b) { return a.d<b.d; } void init(void) { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&w[i]); int tmp,cnt; for (int i=1;i<=n;i++) rk[i].d=w[i],rk[i].id=i; sort(rk+1,rk+n+1,cmp1); tmp=rk[1].d,w[rk[1].id]=cnt=1; for (int i=2;i<=n;i++) { if (tmp^rk[i].d) tmp=rk[i].d,cnt++; w[rk[i].id]=cnt; } int x,y; for (int i=1;i<n;i++) { scanf("%d%d",&x,&y); ins(x,y),ins(y,x); } pre[0][1]=1; DFS(1,1); for (int i=1;i<=K;i++) for (int j=1;j<=n;j++) pre[i][j]=pre[i-1][pre[i-1][j]]; } inline int cmp(Q a,Q b) { return a.l/unit!=b.l/unit?a.l/unit<b.l/unit:a.r<b.r; } inline int min(int i,int j) { return i<j?i:j; } inline int max(int i,int j) { return i>j?i:j; } inline int LCA(int x,int y) { if (dep[x]>dep[y]) x^=y^=x^=y; for (int i=K;i+1;i--) if (dep[y]-dep[x]>=1<<i) y=pre[i][y]; if (x==y) return x; //注意特判x=y时的LCA就是x=y for (int i=K;i+1;i--) if (pre[i][x]^pre[i][y]) x=pre[i][x],y=pre[i][y]; return pre[0][x]; } inline void solve(int i) //"有无"时的写法 { if (has[lst[i]]) { v[w[lst[i]]]--; if (!v[w[lst[i]]]) res--; has[lst[i]]=0; } else { if (!v[w[lst[i]]]) res++; v[w[lst[i]]]++; has[lst[i]]=1; } } void work(void) { int x,y; unit=(int)sqrt(num); //num instead of n for (int i=1;i<=m;i++) { scanf("%d%d",&x,&y); q[i].id=i; if (x==y) q[i].anc=-1; else { q[i].anc=LCA(x,y); if (q[i].anc!=x&&q[i].anc!=y) { q[i].l=min(out[x],out[y]); q[i].r=max(in[x],in[y]); } else { q[i].l=min(in[x],in[y]); q[i].r=max(in[x],in[y]); } //(1)相同 (2)有1个为LCA:两个in (3) 都不同:max(in)&&min(out) } } sort(q+1,q+m+1,cmp); //注意用id弄到ans,不能直接输出 l=1,r=0; for (int i=1;i<=m;i++) if (q[i].anc==-1) ans[q[i].id]=1; else { for (;l<q[i].l;l++) solve(l); for (;q[i].l<l;l--) solve(l-1); for (;r<q[i].r;r++) solve(r+1); for (;q[i].r<r;r--) solve(r); if (lst[q[i].l]==q[i].anc||lst[q[i].r]==q[i].anc) //注意加上lst ans[q[i].id]=res; else { solve(in[q[i].anc]); ans[q[i].id]=res; solve(in[q[i].anc]); } } for (int i=1;i<=m;i++) printf("%d\n",ans[i]); } int main(void) { init(); work(); return 0; }</span>
【小结】
(1) 树上倍增
(2) 树上莫队"有无"的写法,注意3种情况的分类讨论
(3) 排序后的结果要开ans[M]按序输出
(4) Integer是整数的意思,不是-32768--32767。。。
原文地址:http://blog.csdn.net/u013598409/article/details/44943851