zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Gradle 10分钟上手指南
    java并发编程学习: 原子变量(CAS)
    java并发编程学习: 守护线程(Daemon Thread)
    java并发编程学习: 阻塞队列 使用 及 实现原理
    java并发编程学习: ThreadLocal使用及原理
    java并发编程学习:如何等待多个线程执行完成后再继续后续处理(synchronized、join、FutureTask、CyclicBarrier)
    ZooKeeper 笔记(5) ACL(Access Control List)访问控制列表
    gradle项目与maven项目相互转化
    rpc框架之 thrift连接池实现
    java并发编程学习:用 Semaphore (信号量)控制并发资源
  • 原文地址:https://www.cnblogs.com/cjjsb/p/13928366.html
Copyright © 2011-2022 走看看