題解 [LNOI2014]LCA
題目描述
具體做法與心路歷程
這道題擱了有一段時間,趁週六晚上沒有划水的時間裏寫了這道題。這道題。。也算得上是套路吧。
具體做法
這種問題每次詢問一堆點的貢獻一般是把貢獻進行某種轉換,使其與它到祖先這條鏈扯上關係。一般是能夠變成區間修改和區間查詢。
我們看題目要求的是,這個可以直接轉換成把到根的路徑上的權值,然後查詢到根節點上的點權和即可。
這麼轉換後可以考慮沒每次的詢問,把的點權加到鏈上然後查詢到根的鏈上權值和。可以用樹鏈剖分+線段樹來維護。
對於每個詢問的時間複雜度都爲。總的時間複雜度爲,接受不了。
看到題目的我們可以想到差分,即把答案看成 ~ 和 ~ 的貢獻差。
我們可以想到把詢問離線下來後差分,然後枚舉從 ~ ,依次把到根的鏈上的權值,同時處理掉差分在點的詢問,最後輸出答案即可。
邊聽音樂邊碼代碼效率奇高,20min打完一遍過~
/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年11月02日 星期六 21時59分00秒
*******************************/
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
struct IO{
template<typename T>
IO & operator>>(T&res)
{
T q=1;char ch;
while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
res=(ch^48);
while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
res*=q;
return *this;
}
}cin;
struct edge{
int to,next;
edge(int a=0,int b=0):to(a),next(b){}
};
struct Query{
int pos,val,id;
Query(int a=0,int b=0,int c=0):pos(a),val(b),id(c){}
};
const int maxn=5e4+10;
const int mod=201314;
int n,q,head[maxn],cnt,top[maxn],size[maxn],son[maxn],seg[maxn],f[maxn],depth[maxn],ans[maxn];
edge e[maxn<<1];
vector<Query>V[maxn];
void add(int u,int v)
{
e[++cnt]=edge(v,head[u]);
head[u]=cnt;
}
/*{{{線段樹*/
namespace SegmentTree{
int tr[maxn*4],lazy[maxn*4];
void update(int k)
{
tr[k]=tr[k<<1]+tr[k<<1|1];
}
void work(int k,int l,int r,int val)
{
lazy[k]+=val;
tr[k]+=(r-l+1)*val;
}
void pushdown(int k,int l,int r,int mid)
{
if(!lazy[k]) return;
work(k<<1,l,mid,lazy[k]);
work(k<<1|1,mid+1,r,lazy[k]);
lazy[k]=0;
}
void modify(int k,int l,int r,int x,int y,int val)
{
if(l>=x && r<=y) return work(k,l,r,val);
if(l>y || r<x) return ;
int mid=(l+r)>>1;
pushdown(k,l,r,mid);
modify(k<<1,l,mid,x,y,val);
modify(k<<1|1,mid+1,r,x,y,val);
update(k);
}
int query(int k,int l,int r,int x,int y)
{
if(l>=x && r<=y) return tr[k];
if(l>y || r<x) return 0;
int mid=(l+r)>>1;
pushdown(k,l,r,mid);
return (query(k<<1,l,mid,x,y)+query(k<<1|1,mid+1,r,x,y));
}
};
/*}}}*/
/*{{{樹鏈剖分*/
#define v e[i].to
void dfs(int now)
{
depth[now]=depth[f[now]]+1;
size[now]=1;
for(int i=head[now];i;i=e[i].next)
if(v!=f[now])
{
f[v]=now;
dfs(v);
size[now]+=size[v];
if(size[v]>size[son[now]])
son[now]=v;
}
}
void dfs(int now,int tp)
{
seg[now]=++seg[0];
top[now]=tp;
if(son[now])
dfs(son[now],tp);
for(int i=head[now];i;i=e[i].next)
if(v!=f[now] && v!=son[now])
dfs(v,v);
}
void change(int x,int y,int val)
{
while(top[x]!=top[y])
{
if(depth[top[x]]<depth[top[y]]) swap(x,y);
SegmentTree::modify(1,1,seg[0],seg[top[x]],seg[x],val);
x=f[top[x]];
}
if(depth[x]>depth[y]) swap(x,y);
SegmentTree::modify(1,1,seg[0],seg[x],seg[y],val);
}
int ask(int x,int y)
{
int ans=0;
while(top[x]!=top[y])
{
if(depth[top[x]]<depth[top[y]]) swap(x,y);
ans=(ans+SegmentTree::query(1,1,seg[0],seg[top[x]],seg[x]))%mod;
x=f[top[x]];
}
if(depth[x]>depth[y]) swap(x,y);
ans=(ans+SegmentTree::query(1,1,seg[0],seg[x],seg[y]))%mod;
return ans;
}
#undef v
/*}}}*/
int main()
{
/*freopen("p4211.in","r",stdin);*/
/*freopen("p4211.out","w",stdout);*/
cin>>n>>q;
int v;
for(int i=2;i<=n;i++)
{
cin>>v;
add(v+1,i);
}
dfs(1);
dfs(1,1);
int l,r,z;
for(int i=1;i<=q;i++)
{
cin>>l>>r>>z;
l++,r++,z++;
V[l-1].push_back(Query(z,-1,i));
V[r].push_back(Query(z,1,i));
}
int res=0;
for(int i=1;i<=n;i++)
{
change(1,i,1);
for(int j=0;j<V[i].size();j++)
{
res=ask(1,V[i][j].pos);
ans[V[i][j].id]=(ans[V[i][j].id]+V[i][j].val*res+mod)%mod;
}
}
for(int i=1;i<=q;i++)
printf("%d\n",ans[i]);
return 0;
}