zoukankan      html  css  js  c++  java
  • 【GMOJ4488】疯狂动物城

    题目

    题目链接:https://gmoj.net/senior/#main/show/4488

    思路

    其实思路并不难,就是一道码农题罢了 /fad。
    对于一次询问 (x,y),我们设 (operatorname{lca}(x,y)=p),我们把从 (x)(y) 的道路拆成 (x o p)(p o y) 两条(注意 (p) 被计算了两次,最后要减去一次 (p) 的贡献),然后分类讨论:

    • 对于 (x o p) 的点 (k),它到 (y) 的距离就是 (dep[k]+dep[y]-2dep[p]),设 (t=dep[y]-2dep[p]),那么它的贡献就是

    [frac{a_k(dep[k]+t)(dep[k]+t+1)}{2} ]

    化简得

    [frac{a_kdep[k]^2+a_kdep[k](2t+1)+a_k(t^2+t)}{2} ]

    • 对于 (p o y) 的点 (k),它到 (y) 的距离就是 (dep[y]-dep[k]),设 (t=dep[y]),那么它的贡献就是

    [frac{a_k(t-dep[x])(t-dep[x]+1)}{2} ]

    化简得

    [frac{a_kdep[k]^2-a_kdep[k](2t+1)+a_k(t^2+t)}{2} ]

    我们发现,只需要在树上维护链 (sum a_k,sum a_kdep[k],sum a_kdep[k]^2) 这三个信息,询问时乘上 (t) 即可。
    为了方便,我们可以将所有信息乘上 (2),然后在输出的时候再将答案乘上 (2)(mod 20160501) 时的逆元。通过计算可得到 (frac{1}{2}equiv 10080251pmod {20160501})
    然后就是树剖上主席树维护上面三个信息了。
    区间修改时,我们要先预处理出 (sumd[i][1/2]) 表示在树剖之后,编号为 (1sim i) 的点的深度的一次方 / 二次方和,然后区间修改就可以直接用 (delta) 乘上区间 (sumd)
    细节其实挺多的,写题时思路还是要清晰些。
    然后由于我是菜鸡,求 (operatorname{LCA}) 习惯性写了倍增 23333。

    代码

    // QuantAsk YYDS!
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=100010,LG=17,MOD=20160501,MAXN=N*LG*4,inv2=10080251;
    int a[N],head[N],rt[N],dep[N],son[N],size[N],id[N],rk[N],top[N],f[N][LG+1],sumd[N][3];
    int now,n,m,Q,tot,last,opt,X,Y,V;
    
    struct edge
    {
    	int next,to;
    }e[N*2];
    
    void add(int from,int to)
    {
    	e[++tot].to=to;
    	e[tot].next=head[from];
    	head[from]=tot;
    }
    
    void dfs1(int x,int fa)
    {
    	f[x][0]=fa; dep[x]=dep[fa]+1; size[x]=1;
    	for (int i=1;i<=LG;i++)
    		f[x][i]=f[f[x][i-1]][i-1];
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (v!=fa)
    		{
    			dfs1(v,x);
    			size[x]+=size[v];
    			if (size[v]>size[son[x]]) son[x]=v;
    		}
    	}
    }
    
    void dfs2(int x,int tp)
    {
    	top[x]=tp; id[x]=++tot; rk[tot]=x;
    	if (son[x]) dfs2(son[x],tp);
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (v!=f[x][0] && v!=son[x]) dfs2(v,v);
    	}
    }
    
    int lca(int x,int y)
    {
    	if (dep[x]<dep[y]) swap(x,y);
    	for (int i=LG;i>=0;i--)
    		if (dep[f[x][i]]>=dep[y]) x=f[x][i];
    	if (x==y) return x;
    	for (int i=LG;i>=0;i--)
    		if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    
    struct SegTree
    {
    	int tot,lc[MAXN],rc[MAXN],lazy[MAXN],sum[MAXN][3];
    	
    	int New(int now)
    	{
    		int x=++tot;
    		lc[x]=lc[now]; rc[x]=rc[now]; lazy[x]=lazy[now];
    		sum[x][0]=sum[now][0]; sum[x][1]=sum[now][1]; sum[x][2]=sum[now][2];
    		return x;
    	}
    	
    	void pushup(int x)
    	{
    		sum[x][0]=(sum[lc[x]][0]+sum[rc[x]][0])%MOD;
    		sum[x][1]=(sum[lc[x]][1]+sum[rc[x]][1])%MOD;
    		sum[x][2]=(sum[lc[x]][2]+sum[rc[x]][2])%MOD;
    	}
    	
    	void pushdown(int now,int x,int l,int r)
    	{
    		if (lazy[x])
    		{
    			int mid=(l+r)>>1;
    			if (!lc[x] || lc[x]<x) lc[x]=New(lc[now]);
    			if (!rc[x] || rc[x]<x) rc[x]=New(rc[now]);
    			if (lc[x])
    			{
    				sum[lc[x]][0]=(sum[lc[x]][0]+1LL*lazy[x]*(mid-l+1))%MOD;
    				sum[lc[x]][1]=(sum[lc[x]][1]+1LL*lazy[x]*(sumd[mid][1]-sumd[l-1][1]))%MOD;
    				sum[lc[x]][2]=(sum[lc[x]][2]+1LL*lazy[x]*(sumd[mid][2]-sumd[l-1][2]))%MOD;
    				lazy[lc[x]]=(lazy[lc[x]]+lazy[x])%MOD;
    			}
    			if (rc[x])
    			{
    				sum[rc[x]][0]=(sum[rc[x]][0]+1LL*lazy[x]*(r-mid))%MOD;
    				sum[rc[x]][1]=(sum[rc[x]][1]+1LL*lazy[x]*(sumd[r][1]-sumd[mid][1]))%MOD;
    				sum[rc[x]][2]=(sum[rc[x]][2]+1LL*lazy[x]*(sumd[r][2]-sumd[mid][2]))%MOD;
    				lazy[rc[x]]=(lazy[rc[x]]+lazy[x])%MOD;
    			}
    			lazy[x]=0;
    		}
    	}
    	
    	int build(int l,int r)
    	{
    		int x=++tot;
    		if (l==r)
    		{
    			sum[x][0]=a[rk[l]];
    			sum[x][1]=1LL*a[rk[l]]*dep[rk[l]]%MOD;
    			sum[x][2]=1LL*a[rk[l]]*dep[rk[l]]%MOD*dep[rk[l]]%MOD;
    			return x;
    		}
    		int mid=(l+r)>>1;
    		lc[x]=build(l,mid); rc[x]=build(mid+1,r);
    		pushup(x);
    		return x;
    	}
    	
    	int update(int now,int x,int l,int r,int ql,int qr,ll v)
    	{
    		if (!x || x==now) x=New(now);
    		if (l==ql && r==qr)
    		{
    			sum[x][0]=(sum[x][0]+v*(r-l+1))%MOD;
    			sum[x][1]=(sum[x][1]+v*(sumd[r][1]-sumd[l-1][1]))%MOD;
    			sum[x][2]=(sum[x][2]+v*(sumd[r][2]-sumd[l-1][2]))%MOD;
    			lazy[x]=(lazy[x]+v)%MOD;
    			return x;
    		}
    		pushdown(now,x,l,r);
    		int mid=(l+r)>>1;
    		if (qr<=mid) lc[x]=update(lc[now],lc[x],l,mid,ql,qr,v);
    		else if (ql>mid) rc[x]=update(rc[now],rc[x],mid+1,r,ql,qr,v);
    		else lc[x]=update(lc[now],lc[x],l,mid,ql,mid,v),rc[x]=update(rc[now],rc[x],mid+1,r,mid+1,qr,v);
    		pushup(x);
    		return x;
    	}
    	
    	ll query(int x,int l,int r,int ql,int qr,ll t,bool ff)
    	{
    		if (l==ql && r==qr)
    			if (ff) return (1LL*sum[x][0]*(t*t%MOD+t)+1LL*sum[x][1]*(t*2+1)+sum[x][2])%MOD;
    			else return (1LL*sum[x][0]*(t*t%MOD+t)-1LL*sum[x][1]*(t*2+1)+sum[x][2])%MOD;
    		pushdown(x,x,l,r);
    		int mid=(l+r)>>1;
    		if (qr<=mid) return query(lc[x],l,mid,ql,qr,t,ff);
    		if (ql>mid) return query(rc[x],mid+1,r,ql,qr,t,ff);
    		return (query(lc[x],l,mid,ql,mid,t,ff)+query(rc[x],mid+1,r,mid+1,qr,t,ff))%MOD;
    	}
    }seg;
    
    void Update(int x,int y)
    {
    	while (dep[top[x]]>dep[y])
    	{
    		rt[m]=seg.update(rt[now],rt[m],1,n,id[top[x]],id[x],V);
    		x=f[top[x]][0];
    	}
    	rt[m]=seg.update(rt[now],rt[m],1,n,id[y],id[x],V);
    }
    
    ll Query(int x,int y,ll t,bool ff)
    {
    	ll s=0;
    	while (dep[top[x]]>dep[y])
    	{
    		s=(s+seg.query(rt[now],1,n,id[top[x]],id[x],t,ff))%MOD;
    		x=f[top[x]][0];
    	}
    	return (s+seg.query(rt[now],1,n,id[y],id[x],t,ff))%MOD;
    }
    
    int main()
    {
    	freopen("zootopia.in","r",stdin);
    	freopen("zootopia.out","w",stdout);	
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&n,&Q);
    	for (int i=1,x,y;i<n;i++)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y); add(y,x);
    	}
    	tot=now=0;
    	dfs1(1,0); dfs2(1,1);
    	for (int i=1;i<=n;i++)
    	{
    		sumd[i][1]=(sumd[i-1][1]+dep[rk[i]])%MOD;
    		sumd[i][2]=(sumd[i-1][2]+1LL*dep[rk[i]]*dep[rk[i]])%MOD;
    	}
    	for (int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	rt[0]=seg.build(1,n);	
    	while (Q--)
    	{
    		scanf("%d",&opt);
    		if (opt==1)
    		{
    			scanf("%d%d%d",&X,&Y,&V);
    			X^=last; Y^=last;
    			int p=lca(X,Y);
    			rt[++m]=0;
    			Update(X,p); Update(Y,p);
    			rt[m]=seg.update(rt[now],rt[m],1,n,id[p],id[p],-V);
    			now=m;
    		}
    		if (opt==2)
    		{
    			scanf("%d%d",&X,&Y);
    			X^=last; Y^=last;
    			int p=lca(X,Y);
    			last=(Query(X,p,dep[Y]-2LL*dep[p],1)+Query(Y,p,dep[Y],0)-seg.query(rt[now],1,n,id[p],id[p],dep[Y],0))*inv2%MOD;
    			last=(last+MOD)%MOD;
    			printf("%d
    ",last);
    		}
    		if (opt==3) 
    		{
    			scanf("%d",&now);
    			now^=last;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    关于使用CodeFirst,修改类或上下文时操作数据库报错解决方法
    解决:启用多线程调用webBrowsers函数报错:指定的转换无效
    强制IE浏览器或WebBrowser控件使用指定版本显示网页
    EF:分页查询 + 条件查询 + 排序
    (.NET高级课程笔记)委托、事件总结
    windows server 2008 磁盘挂载
    Git的使用
    VLAN原理详解
    Jumpserver的部署和使用
    进程与线程的理解
  • 原文地址:https://www.cnblogs.com/stoorz/p/13894636.html
Copyright © 2011-2022 走看看