• Luogu P1600 天天爱跑步


    昨天的原题大战考到了这题,发现我之前竟然没做过就顺便水篇博客

    首先对于一条路径(x o y),我们显然可以根据它们的(operatorname{LCA})把路径分成两段

    • 对于路径(x o z),我们发现上面的所有点满足时间与深度之和不变
    • 对于路径(z o y),我们发现上面的所有点满足时间与深度之差不变

    那么很自然我们想到分别维护两种路径,不过要注意(z)不要算重了

    现在就以第一种和的路径为例,我们发现树上路径加增量的操作显然可以直接差分

    由于差分之后需要求的是子树和,我们很容易想到把时间与深度之和作为下标建线段树,每次上传直接线段树合并即可

    总复杂度(O(nlog n)),就是要注意内存别MLE了

    #include<cstdio>
    #include<iostream>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=300005,SZ=1e7,P=20;
    struct edge
    {
    	int to,nxt;
    }e[N<<1]; int n,m,head[N],cnt,x,y,w[N],dep[N],anc[N][P],ans[N],rt1[N],rt2[N];
    inline void addedge(CI x,CI y)
    {
    	e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
    	e[++cnt]=(edge){x,head[y]}; head[y]=cnt;
    }
    class Segment_Tree
    {
    	private:
    		struct segment
    		{
    			int ch[2],sum;
    		}node[SZ]; int tot;
    	public:
    		#define lc(x) node[x].ch[0]
    		#define rc(x) node[x].ch[1]
    		#define S(x) node[x].sum
    		#define TN CI l=0,CI r=3*n
    		inline void insert(int& now,CI pos,CI mv,TN)
    		{
    			if (!now) now=++tot; if (l==r) return (void)(S(now)+=mv); int mid=l+r>>1;
    			if (pos<=mid) insert(lc(now),pos,mv,l,mid); else insert(rc(now),pos,mv,mid+1,r);
    		}
    		inline void merge(int& x,CI y,TN)
    		{
    			if (!y) return; if (!x) return (void)(x=y); S(x)+=S(y);
    			int mid=l+r>>1; merge(lc(x),lc(y),l,mid); merge(rc(x),rc(y),mid+1,r);
    		}
    		inline int query(CI now,CI pos,TN)
    		{
    			if (!now) return 0; if (l==r) return S(now); int mid=l+r>>1;
    			if (pos<=mid) return query(lc(now),pos,l,mid); else return query(rc(now),pos,mid+1,r);
    		}
    		#undef lc
    		#undef rc
    		#undef S
    		#undef TN
    }SEG1,SEG2;
    #define to e[i].to
    class Double_Increased
    {
    	public:
    		inline int getlca(int x,int y)
    		{
    			if (dep[x]<dep[y]) swap(x,y); RI i;
    			for (i=P-1;~i;--i) if (dep[anc[x][i]]>=dep[y]) x=anc[x][i];
    			if (x==y) return x; for (i=P-1;~i;--i)
    			if (anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
    			return anc[x][0];
    		}
    		inline void DFS1(CI now=1,CI fa=0)
    		{
    			dep[now]=dep[anc[now][0]=fa]+1; RI i; for (i=0;i<P-1;++i)
    			if (anc[now][i]) anc[now][i+1]=anc[anc[now][i]][i]; else break;
    			for (i=head[now];i;i=e[i].nxt) if (to!=fa) DFS1(to,now);
    		}
    		inline void DFS2(CI now=1,CI fa=0)
    		{
    			for (RI i=head[now];i;i=e[i].nxt) if (to!=fa)
    			DFS2(to,now),SEG1.merge(rt1[now],rt1[to]),SEG2.merge(rt2[now],rt2[to]);
    			ans[now]=SEG1.query(rt1[now],n+dep[now]+w[now])+SEG2.query(rt2[now],n+dep[now]-w[now]);
    		}
    }T;
    #undef to
    int main()
    {
    	//freopen("race.in","r",stdin); freopen("race.out","w",stdout);
    	RI i; for (scanf("%d%d",&n,&m),i=1;i<n;++i)
    	scanf("%d%d",&x,&y),addedge(x,y);
    	for (i=1;i<=n;++i) scanf("%d",&w[i]);
    	for (T.DFS1(),i=1;i<=m;++i)
    	{
    		scanf("%d%d",&x,&y); int z=T.getlca(x,y);
    		SEG1.insert(rt1[x],n+dep[x],1); if (z!=1) SEG1.insert(rt1[anc[z][0]],n+dep[x],-1);
    		SEG2.insert(rt2[z],n+dep[z]-(dep[x]-dep[z]),-1);
    		SEG2.insert(rt2[y],n+dep[z]-(dep[x]-dep[z]),1);
    	}
    	for (T.DFS2(),i=1;i<=n;++i) printf("%d ",ans[i]); return 0;
    }
    
  • 相关阅读:
    MFC Windows 程序设计>WinMain 简单Windows程序 命令行编译
    AT3949 [AGC022D] Shopping 题解
    CF643D Bearish Fanpages 题解
    CF643C Levels and Regions 题解
    CF241E Flights 题解
    CF671C Ultimate Weirdness of an Array 题解
    CF1592F Alice and Recoloring 题解
    GYM 102452E 题解
    CF494C Helping People 题解
    P5556 圣剑护符
  • 原文地址:https://www.cnblogs.com/cjjsb/p/13928366.html
Copyright © 2020-2023  润新知