• 天天爱跑步


    天天爱跑步

    一道:树上差分+LCA+桶的题

    说实话,这道题放在D1T2就是非常不合理的。然而CCF就是放了,并且还是能依靠CSP捞钱,你也必须交钱参加比赛。这个社会是多么的不公啊!闲扯结束

    显然如果对每条路径都进行一次处理,复杂度不对。考虑对路径进行一次预处理,然后进行统一的计算答案。我们发现当一条路径对某一个点P产生贡献时满足这个条件:

    1.当(P)在该路径的(S)(LCA)之间时 (dep[S]-dep[P]=watch[P]) 移一下项为(dep[S]=dep[P]+watch[P])那么该路径起点对P有贡献

    2.当(P)在该路径的(LCA)(T)之间时 (dist[S,T]-watch[P]=dep[T]-dep[P]) 移一下项(dist[S,T]-dep[T]=watch[P]-dep[P])那么该路径终点对P有贡献

    然后用一个全局桶维护这些贡献 当枚举到当前点的时候 将当前点作为终点和起点的贡献加在桶里面。然后计算当前点的ans

    细节:一个它只会受其子树的贡献,其他的贡献不会有的。如何处理?在开始遍历这个点的时候用(ad1,ad2)来存储已经存在的桶里面的值 然后遍历子树 当又返回到当前节点的时候 计算当前节点的答案。显然当前节点的答案为(ans[x]+=bas1[watch[x]+dep[x]]-ad1+bas2[watch[x]-dep[x]+maxn]-ad2)

    注意到我们对(bas2)的计算的时候加了一个(maxn) ,其实原因很显然 (watch[x]-dep[x])有可能小于零

    还有就是当遍历完当前点 应该将以当前点为LCA的路径的影响全部消除。因为如果以当前点为LCA,显然不会对当前点的父亲及祖先产生影响,应该减掉

    然后就是代码了QAQ 我是用的邻接表存储的以当前节点为终点的路径编号,以及以当前节点为LCA的路径编号

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #define int long long 
    using namespace std;
    const int maxn=500010;
    int read(){
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    int n,m,fir[maxn],nxt[maxn<<1],to[maxn<<1],tot=1,watch[maxn],fa[maxn][25];
    int st[maxn],ed[maxn],stt[maxn],dist[maxn],ans[maxn],dep[maxn];
    int fir1[maxn],nxt1[maxn<<1],to1[maxn<<1],tot1,fir2[maxn],nxt2[maxn<<1],to2[maxn<<1],tot2;
    void add(int x,int y){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;}
    void dfs1(int x){
    	dep[x]=dep[fa[x][0]]+1;
    	for(int i=0;i<=20;i++)
    		fa[x][i+1]=fa[fa[x][i]][i];
    	for(int i=fir[x];i;i=nxt[i]){
    		int y=to[i];if(y==fa[x][0]) continue;
    		fa[y][0]=x;dfs1(y);
    	}
    }
    int LCA(int x,int y){
    	if(dep[x]<dep[y]) swap(x,y);
    	for(int i=20;i>=0;i--){
    		if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    		if(x==y) return x;
    	}
    	for(int i=20;i>=0;i--){
    		if(fa[x][i]!=fa[y][i]){
    			x=fa[x][i];y=fa[y][i];
    		}
    	}
    	return fa[x][0];
    }
    void add1(int x,int y){nxt1[++tot1]=fir1[x];fir1[x]=tot1;to1[tot1]=y;}
    void add2(int x,int y){nxt2[++tot2]=fir2[x];fir2[x]=tot2;to2[tot2]=y;}
    int bas1[maxn<<1],bas2[maxn<<1],ad1,ad2;
    void dfs2(int x){
    	ad1=bas1[watch[x]+dep[x]];ad2=bas2[watch[x]-dep[x]+maxn];
    	for(int i=fir[x];i;i=nxt[i]){
    		int y=to[i];if(y==fa[x][0]) continue;
    		dfs2(y);
    	}
    	bas1[dep[x]]+=stt[x];
    	for(int i=fir1[x];i;i=nxt1[i]){
    		int y=to1[i];
    		bas2[dist[y]-dep[ed[y]]+maxn]++;
    	}
    	ans[x]+=bas1[watch[x]+dep[x]]-ad1+bas2[watch[x]-dep[x]+maxn]-ad2;
    	for(int i=fir2[x];i;i=nxt2[i]){
    		int y=to2[i];
    		bas1[dep[st[y]]]--;
    		bas2[dist[y]-dep[ed[y]]+maxn]--;
    	}
    }
    signed main(){
    	n=read();m=read();
    	for(int i=1,x,y;i<=n-1;i++){
    		x=read();y=read();
    		add(x,y);add(y,x);
    	}
    	for(int i=1;i<=n;i++) watch[i]=read();
    	dep[1]=1;
    	dfs1(1);
    	for(int i=1,x,y,lca;i<=m;i++){
    		x=read();y=read();
    		lca=LCA(x,y);st[i]=x;ed[i]=y;
    		dist[i]=dep[x]+dep[y]-2*dep[lca];
    		stt[x]++;add1(y,i);add2(lca,i);
    		if(dep[lca]+watch[lca]==dep[x]) ans[lca]--;
    	}
    	dfs2(1);
    	for(int i=1;i<=n;i++)
    		printf("%lld ",ans[i]);
    }
    
  • 相关阅读:
    JSON特殊字符的处理
    java中高并发和高响应解决方法
    对redis深入理解
    对java中arraylist深入理解
    Redis的字典扩容与ConcurrentHashMap的扩容策略比较
    PHP压缩上传图片
    windows 平台 php_Imagick 拓展遇到的那些坑!
    windows7下php5.4成功安装imageMagick,及解决php imagick常见错误问题。(phpinfo中显示不出来是因为:1.imagick软件本身、php本身、php扩展三方版本要一致,2.需要把CORE_RL_*.dll多个文件放到/php/目录下面)
    php使用imagick模块实现图片缩放、裁剪、压缩示例
    文件打包,下载之使用PHP自带的ZipArchive压缩文件并下载打包好的文件
  • 原文地址:https://www.cnblogs.com/mendessy/p/11795123.html
Copyright © 2020-2023  润新知