• [GXOI/GZOI2019] 旧词


    前言

    挺有意思,写出来一遍过挺爽的,于是水沝淼㵘一篇博客

    题目

    洛谷

    LOJ

    讲解

    先看弱化版[LNOI2014]LCA

    之前的思路是考虑LCA到根上每个点的贡献,用树链剖分维护即可

    类似地,我们考虑先对询问按 (x) 从小到大排序

    对于每个询问达到 (x) 时,我们对其到根的路径上加上权值

    由于本题有一个 (k) 次方,所以我们不能再加 (1),而是应该加上(depth^k(i)-depth^k(i-1))

    这样我们如果求出 (y) 到根的路径上的点权和,即求出了答案

    我们依然可以区间加一(懒标记),只是每个节点加的权值为上面的那个式子,可以预处理

    代码

    int qpow(int x,int y)
    {
    	int ret = 1;
    	while(y){if(y & 1) ret = 1ll * ret * x % MOD;x = 1ll * x * x % MOD;y >>= 1;}
    	return ret;
    }
    
    int head[MAXN],tot;
    struct edge
    {
    	int v,nxt;
    }e[MAXN];
    void Add_Edge(int x,int y)
    {
    	e[++tot].v = y;
    	e[tot].nxt = head[x];
    	head[x] = tot;
    }
    
    int d[MAXN],siz[MAXN],son[MAXN],f[MAXN];
    void dfs1(int x)
    {
    	d[x] = d[f[x]] + 1;
    	siz[x] = 1;
    	for(int i = head[x]; i ;i = e[i].nxt)
    	{
    		dfs1(e[i].v);
    		siz[x] += siz[e[i].v];
    		if(siz[e[i].v] > siz[son[x]]) son[x] = e[i].v;
    	}
    }
    int tp[MAXN],dfn[MAXN],dfntot,rdfn[MAXN];
    void dfs2(int x,int t)
    {
    	dfn[x] = ++dfntot;
    	rdfn[dfntot] = x;
    	tp[x] = t;
    	if(!son[x]) return;
    	dfs2(son[x],t);
    	for(int i = head[x]; i ;i = e[i].nxt)
    		if(e[i].v != son[x])
    			dfs2(e[i].v,e[i].v);
    }
    
    #define lc (x<<1)
    #define rc (x<<1|1)
    struct SegmentTree
    {
    	struct node
    	{
    		int pre,s,lz;
    	}t[MAXN << 2];
    	
    	void down(int x)
    	{
    		if(!t[x].lz) return;
    		t[lc].s = (t[lc].s + 1ll * t[lc].pre * t[x].lz) % MOD;
    		t[rc].s = (t[rc].s + 1ll * t[rc].pre * t[x].lz) % MOD;
    		t[lc].lz += t[x].lz; t[rc].lz += t[x].lz;
    		t[x].lz = 0;
    	}
    	
    	void up(int x)
    	{
    		t[x].s = (t[lc].s + t[rc].s) % MOD;
    	}
    	
    	void Build(int x,int l,int r)
    	{
    		if(l == r)
    		{
    			t[x].pre = (qpow(d[rdfn[l]],k) - qpow(d[rdfn[l]]-1,k) + MOD) % MOD;
    			return;
    		}
    		int mid = (l+r) >> 1;
    		Build(lc,l,mid); Build(rc,mid+1,r);
    		t[x].pre = (t[lc].pre + t[rc].pre) % MOD;
    	}
    	
    	void Add(int x,int l,int r,int ql,int qr)
    	{
    		if(ql <= l && r <= qr)
    		{
    			t[x].lz++;
    			t[x].s = (t[x].pre + t[x].s) % MOD;
    			return;
    		}
    		down(x);
    		int mid = (l+r) >> 1;
    		if(ql <= mid) Add(lc,l,mid,ql,qr);
    		if(mid+1 <= qr) Add(rc,mid+1,r,ql,qr);
    		up(x);
    	}
    	
    	int Query(int x,int l,int r,int ql,int qr)
    	{
    		if(ql <= l && r <= qr) return t[x].s;
    		down(x);
    		int mid = (l+r) >> 1,ret = 0;
    		if(ql <= mid) ret += Query(lc,l,mid,ql,qr);
    		if(mid+1 <= qr) ret += Query(rc,mid+1,r,ql,qr);
    		return ret % MOD;
    	}
    }st;
    
    void AddChain(int x) 
    {
    	while(x)
    	{
    		st.Add(1,1,n,dfn[tp[x]],dfn[x]);
    		x = f[tp[x]];
    	}
    }
    int QueryChain(int x)
    {
    	int ret = 0;
    	while(x)
    	{
    		ret = (ret + st.Query(1,1,n,dfn[tp[x]],dfn[x])) % MOD;
    		x = f[tp[x]];
    	}
    	return ret;
    }
    
    struct query
    {
    	int x,y,ID;
    	bool operator < (const query &px)const{
    		return x < px.x;
    	}
    }q[MAXN];
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read(); Q = Read(); k = Read();
    	for(int i = 2;i <= n;++ i) Add_Edge(f[i] = Read(),i);
    	for(int i = 1;i <= Q;++ i) q[i].x = Read(),q[i].y = Read(),q[i].ID = i;
    	sort(q+1,q+Q+1);
    	dfs1(1);
    	dfs2(1,1);
    	st.Build(1,1,n);
    	int now = 1;
    	for(int i = 1;i <= Q;++ i)
    	{
    		while(now <= q[i].x) AddChain(now++);
    		ans[q[i].ID] = QueryChain(q[i].y);
    	}
    	for(int i = 1;i <= Q;++ i) Put(ans[i],'
    ');
    	return 0;
    }
    
  • 相关阅读:
    Springboot日志初探
    Slf4j初探
    Log4j日志初探
    Nacos集群初探
    python初学者必看学习路线图!!!
    ubuntu无法设置为中文怎么办?(适用于ubuntu14.04/16.04)
    PythonGUI编程(Tkinter)-基本概念以及核心开发步骤
    Python学生信息管理系统(注释最详细,小白都看的懂)
    Python面向对象分析存放家具
    面向对象分析烤地瓜项目
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/14237986.html
Copyright © 2020-2023  润新知