• 【uoj#261】【NOIP2016】天天爱跑步


    每个人可以分为往上和往下两部分,我们将向上的路径和向下的命名为第一种路径和第二种路径。
    询问就是求经过某个点的,出发点深度为d-w的第一种路径数量,加上出发点深度为d+w的第二种路径的数量。
    我们开个数组记录每个深度的答案。对于每条路径,我们都在下面那个点加,在上面那个点减(注意加和减都是出发点的深度上),询问就变成了求子树和。
    我们可以通过dfs来求答案,dfs到一个点的时候,记下之前在所求深度上的答案,加完这棵子树后,新答案与原来的差就是这个点真正的答案。
    (关于第二种路径我是先延长上去再减掉上面一段,为此还从根节点往上加了n个点)

    以上是我去年(看了题解)写的做法,但是。。。
    反正时间复杂度都是O(nlogn),是不是以d-t或d+t为下标,然后就变成链加单点求和,然后线段树合并或者dsu就行了?感觉这样无脑一点?
    没写过不知道是不是对的。。。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define mxn 2000010
    using namespace std;
    int n,m,q,x,y,tot,head[mxn],p[mxn][21],dep[mxn],id[mxn],siz[mxn],a[mxn],ans[mxn];
    int hd[mxn],nxt[mxn],cnt[mxn],sum[mxn],S[mxn],T[mxn],typ[mxn];
    struct ed{int to,nxt;}edge[mxn<<1];
    void addedge(int u,int v){
    	edge[++m]=(ed){v,head[u]};head[u]=m;
    	edge[++m]=(ed){u,head[v]};head[v]=m;
    }
    void dfs(int u,int fa,int d){
    	p[u][0]=fa;dep[u]=d;siz[u]=1;id[u]=++tot;int v;
    	for (int i=head[u];i;i=edge[i].nxt)
    		if ((v=edge[i].to)!=fa)
    			dfs(v,u,d+1),siz[u]+=siz[v];
    }
    int lca(int x,int y){
    	if (dep[x]<dep[y]) {int t=x;x=y;y=t;}
    	int k=log2(dep[x]);
    	for (int i=k;i>=0;--i)
    		if (dep[x]-(1<<i)>=dep[y]) x=p[x][i];
    	if (x==y) return x;
    	for (int i=k;i>=0;--i)
    		if (p[x][i]!=p[y][i]) x=p[x][i],y=p[y][i];
    	return p[x][0];
    }
    void dfs1(int u,int fa){//up
    	int num=cnt[dep[u]+a[u]],v; 
    	cnt[dep[u]]+=sum[u];
    	for (int i=head[u];i;i=edge[i].nxt)
    		if ((v=edge[i].to)!=fa) dfs1(v,u);
    	ans[u]+=cnt[dep[u]+a[u]]-num;
    	for (int j=hd[u];j;j=nxt[j]) --cnt[dep[S[j]]];
    }
    void dfs2(int u,int fa){//down add
    	int num=cnt[dep[u]-a[u]],v;
    	for (int j=hd[u];j;j=nxt[j]) ++cnt[dep[S[j]]];
    	for (int i=head[u];i;i=edge[i].nxt)
    		if ((v=edge[i].to)!=fa) dfs2(v,u);
    	ans[u]+=cnt[dep[u]-a[u]]-num;
    	cnt[dep[u]]-=sum[u];
    }
    void dfs3(int u,int fa){//down minus
    	int num=cnt[dep[u]-a[u]],v;
    	for (int j=hd[u];j;j=nxt[j]) ++cnt[dep[S[j]]];
    	for (int i=head[u];i;i=edge[i].nxt)
    		if ((v=edge[i].to)!=fa) dfs3(v,u);
    	ans[u]-=cnt[dep[u]-a[u]]-num;
    	cnt[dep[u]]-=sum[u];
    }
    int main()
    {
    	scanf("%d%d",&n,&q);
    	for (int i=1;i<n;++i)
    		scanf("%d%d",&x,&y),addedge(x,y);
    	addedge(1,n+1);
    	for (int i=1;i<n;++i)
    		addedge(n+i,n+i+1);
    	dfs(n*2,n*2,1);x=log2(n)+1;m=0;
    	for (int j=1;j<=x;++j)
    		for (int i=1;i<=n*2;++i)
    			p[i][j]=p[p[i][j-1]][j-1];
    	for (int i=1;i<=n;++i) scanf("%d",&a[i]);
    	for (int i=1;i<=q;++i){
    		scanf("%d%d",&x,&y);
    		int z=lca(x,y),d=dep[z]*2-dep[x],k=log2(dep[x]-dep[z]+1),w=z;
    		for (int j=k;j>=0;--j)
    			if (dep[w]-(1<<j)>=d) w=p[w][j];
    		S[++m]=x,T[m]=z,typ[m]=1;
    		S[++m]=w,T[m]=y,typ[m]=2;
    		S[++m]=w,T[m]=z,typ[m]=3;
    	}
    	for (int i=1;i<=m;++i)
    		if (typ[i]==1)
    			nxt[i]=hd[T[i]],hd[T[i]]=i,++sum[S[i]];
    	dfs1(n*2,n*2);
    	memset(cnt,0,sizeof(cnt));
    	memset(sum,0,sizeof(sum));
    	memset(hd,0,sizeof(hd));
    	for (int i=1;i<=m;++i)
    		if (typ[i]==2)
    			nxt[i]=hd[T[i]],hd[T[i]]=i,++sum[S[i]];
    	dfs2(n*2,n*2);
    	memset(cnt,0,sizeof(cnt));
    	memset(sum,0,sizeof(sum));
    	memset(hd,0,sizeof(hd));
    	for (int i=1;i<=m;++i)
    		if (typ[i]==3)
    			nxt[i]=hd[T[i]],hd[T[i]]=i,++sum[S[i]];
    	dfs3(n*2,n*2);
    	for (int i=1;i<=n;++i)
    		printf("%d ",ans[i]);
    	puts("");
    	return 0;
    }
    
  • 相关阅读:
    jsoncpp使用
    java学习笔记12--国际化
    java学习笔记13--比较器(Comparable、Comparator)
    java学习笔记15--引用传递
    java学习笔记16--异常
    Java学习笔记——File类之文件管理和读写操作、下载图片
    java学习笔记3
    我的二十一天CoreJava 学习笔记
    大数据处理方面的 7 个开源搜索引擎
    GridView编辑删除操作
  • 原文地址:https://www.cnblogs.com/zzqtxdy/p/11488695.html
Copyright © 2020-2023  润新知