解法一:
∑
i
∈
[
1
,
n
]
a
n
d
x
[
i
]
∈
[
l
,
r
]
d
i
s
(
u
,
i
)
=
∑
i
d
e
p
[
u
]
+
d
e
p
[
i
]
−
2
∗
d
e
p
[
l
c
a
(
u
,
i
)
]
\sum_{i\in [1,n] \ and \ x[i]\in[l,r]} dis(u,i) = \sum_{i}dep[u]+dep[i]-2*dep[lca(u,i)]
i∈[1,n] and x[i]∈[l,r]∑dis(u,i)=i∑dep[u]+dep[i]−2∗dep[lca(u,i)]
从这个式子我们可以将答案表达为:
[
L
,
R
]
[L,R]
[L,R]中妖怪数量*u的深度+这些妖怪深度之和-
2
∗
2*
2∗他们与u的lca的深度之和。
前面两个比较简单,重要的是第3个。
这也是一种套路了。
两个点的lca的深度等于他们到根的路径的并的长度。
对于这个题我们可以更具体一点:
我们统计每个点被两个点同时经过的次数。
那么每个点的次数 * 这个点到它父亲的距离 的和 = lca的深度。
对于多个与u匹配的点
x
1
,
x
2...
x1,x2...
x1,x2...,我们可以用树剖+线段树把被
x
i
x_i
xi经过的次数累加统计。
回答询问时再拿
u
u
u往上跑统计答案。
但是我们需要的是
[
L
,
R
]
[L,R]
[L,R]之间的答案。
那么就用可持久化线段树就行了。
等等这是个区间修改区间查询,主席树空间会炸?!。
可以标记永久化。
通过这个题总算是知道了标记永久化的正确姿势。
以前打标记永久化都是区间修改单点查询,以为就是经过标记时处理一下就行了。
实际上是经过标记时处理。线段被整个覆盖时需要把下面更深的所有线段的贡献也加上,但这个贡献都是覆盖一个整的线段的,可以在打标记时处理出来。
具体看代码。
AC Code:
#include<bits/stdc++.h>
#define maxn 300005
#define maxpt maxn * 50
#define LL long long
using namespace std;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
int n,q,A,x[maxn];
int info[maxn],Prev[maxn<<1],to[maxn<<1],cst[maxn<<1],cnt_e=0;
void Node(int u,int v,int w){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=w; }
LL val[maxn],sum[maxn],s2[maxn];
int siz[maxn],son[maxn],tp[maxn],id[maxn],fa[maxn],dep[maxn],tot;
void dfs1(int now,int ff){
siz[now]=1,son[now]=-1;
fa[now] = ff;
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff){
dep[to[i]] = dep[now] + cst[i];
dfs1(to[i],now);
if(son[now] == -1 || siz[son[now]] < siz[to[i]])
son[now] = to[i];
siz[now] += siz[to[i]];
}
}
void dfs2(int now,int ff){
id[now] = ++tot;
if(son[now]!=-1) tp[son[now]]=tp[now],dfs2(son[now],now);
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff && to[i]!=son[now]){
tp[to[i]] = to[i];
dfs2(to[i],now);
}
}
void dfs3(int now,int ff){
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff){
val[id[to[i]]] = cst[i];
dfs3(to[i],now);
}
}
bool cmp(const int &u,const int &v){ return x[u]<x[v]; }
int c[maxn],sb[maxn];
int rt[maxn],lc[maxpt],rc[maxpt],tag[maxpt];
LL Sum[maxpt],Val[maxpt];
void upd(int now){
Sum[now] = Sum[lc[now]] + Sum[rc[now]] + Val[now];
}
void Insert(int &now,int l,int r,int ql,int qr){
if(qr<l || r<ql) return;
tag[++tot] = tag[now] , Sum[tot] = Sum[now] , Val[tot] = Val[now] , lc[tot] = lc[now] ,rc[tot] = rc[now];
now = tot;
if(ql<=l && r<=qr){ tag[now]++,Val[now]+=val[r]-val[l-1],upd(now);return;}
int mid = (l+r) >> 1;
Insert(lc[now],l,mid,ql,qr);
Insert(rc[now],mid+1,r,ql,qr);
upd(now);
}
void Insertpath(int now,int &rt){
for(;now;now=fa[tp[now]])
Insert(rt,1,n,id[tp[now]],id[now]);
}
LL query(int now,int l,int r,int ql,int qr){
if(!now || l>qr || ql>r) return 0;
if(ql<=l&&r<=qr) return Sum[now];
int mid = (l+r) >> 1;
LL ret = tag[now] * (val[min(r,qr)]-val[max(l,ql)-1]);
ret += query(lc[now],l,mid,ql,qr) + query(rc[now],mid+1,r,ql,qr);
return ret;
}
LL querypath(int now,int L,int R){
LL ret = 0;
for(;now;now=fa[tp[now]]){
//printf("%lld %lld\n",query(rt[R],1,A,id[tp[now]],id[now]),query(rt[L],1,A,id[tp[now]],id[now]));
ret += query(rt[R],1,n,id[tp[now]],id[now]) - query(rt[L],1,n,id[tp[now]],id[now]);
}
return ret;
}
int main(){
read(n),read(q),read(A);
for(int i=1;i<=n;i++) read(x[i]),sb[++sb[0]]=x[i],c[i]=i;
sort(sb+1,sb+1+sb[0]);
sb[0] = unique(sb+1,sb+1+sb[0])-sb-1;
for(int i=1;i<=n;i++) x[i]=lower_bound(sb+1,sb+1+sb[0],x[i])-sb;
for(int i=1,u,v,w;i<n;i++){
read(u),read(v),read(w);
Node(u,v,w),Node(v,u,w);
}
dfs1(1,0),tp[1]=1,dfs2(1,0),dfs3(1,0);tot=0;
for(int i=1;i<=n;i++) val[i] += val[i-1];
sort(c+1,c+1+n,cmp);
for(int i=1;i<=n;i++)
{
if(i>1 && x[c[i]] != x[c[i-1]]) rt[x[c[i]]] = rt[x[c[i]]-1];
//if(x[c[i]] == 3)
// printf("%d\n",rt[x[c[i]]]);
Insertpath(c[i],rt[x[c[i]]]);
sum[x[c[i]]]++,s2[x[c[i]]]+=dep[c[i]];
}
for(int i=1;i<=sb[0];i++) sum[i]+=sum[i-1],s2[i]+=s2[i-1];
//printf("s2[%d] = %lld\n",i,s2[i]);
LL ans=0;
for(int u,a,b,L,R;q--;){
read(u),read(a),read(b);
L=min((a+ans)%A,(b+ans)%A),R=max((a+ans)%A,(b+ans)%A);
//printf("%d %d\n",L,R);
L = lower_bound(sb+1,sb+1+sb[0],L)-sb-1, R = upper_bound(sb+1,sb+1+sb[0],R)-sb-1;
//printf("%d %d\n",L,R);
ans = (sum[R] - sum[L]) * dep[u] + s2[R] - s2[L];
//printf("%lld\n",ans);
ans -= 2 * querypath(u,L,R);
printf("%lld\n",ans);
}
}
解法二:
动态边分治。
这个树所有顶点的度数都小于或等于3
题目明示。
所以不需要加虚点,记所有点在每一层的所属就行。
每层用动态开店线段树求区间和。?!
直接
v
e
c
t
o
r
<
i
n
t
,
l
o
n
g
l
o
n
g
>
vector<int,long\ long>
vector<int,long long>
前面存年龄,后面存距离的前缀和,
l
o
w
e
r
b
o
u
n
d
lowerbound
lowerbound大法好。
感觉比动态点分治好写多了。。。
感觉比点分治好写多了。
感觉比分治好些多了。
感觉好写多了。
我再也不会写点分治了
具体看短得要命的代码。
AC Code:
#include<bits/stdc++.h>
#define maxn 150005
#define lim 20
#define LL long long
using namespace std;
int n,q,A,x[maxn];
LL cst[maxn<<1];
int info[maxn],Prev[maxn<<1],to[maxn<<1],cnt_e=1;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=c; }
bool vis[maxn<<1];
int Min = 0 , rt , siz[maxn];
void dfs(int now , int ff , int tsz){
siz[now] = 1;
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff && !vis[i]){
dfs(to[i],now,tsz);
siz[now] += siz[to[i]];
if(Min > max(tsz - siz[to[i]] , siz[to[i]]))
Min = max(tsz - siz[to[i]] , siz[to[i]]),
rt = i;
}
}
int findrt(int now,int tsz){
rt = -1 , Min = 0x3f3f3f3f;
dfs(now,0,tsz);
return rt;
}
vector<pair<int,LL> >vec[maxn<<1][2];
int sid[maxn][lim],fa[maxn][lim],Dep[maxn];
LL fadis[maxn][lim];
void ser(int now,int ff,LL dis,vector<pair<int,LL> >&vec,int side,int dep,int pfa){
vec.push_back(make_pair(x[now],dis));
sid[now][dep] = side , fa[now][dep] = pfa , fadis[now][dep] = dis;
siz[now] = 1;
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff && !vis[i])
ser(to[i],now,dis+cst[i],vec,side,dep,pfa),
siz[now] += siz[to[i]];
}
void Solve(int now,int dep){
//printf("%d %d\n",now,dep);
vis[now] = vis[now^1] = 1;
ser(to[now],0,0,vec[now][0],0,dep,now);
sort(vec[now][0].begin(),vec[now][0].end());
for(int i=1,siz=vec[now][0].size();i<siz;i++) vec[now][0][i].second += vec[now][0][i-1].second;
ser(to[now^1],0,0,vec[now][1],1,dep,now);
sort(vec[now][1].begin(),vec[now][1].end());
for(int i=1,siz=vec[now][1].size();i<siz;i++) vec[now][1][i].second += vec[now][1][i-1].second;
int rt1 = findrt(to[now],siz[to[now]]);
if(rt1 != -1) Solve(rt1,dep+1);
else Dep[to[now]] = dep;
rt1 = findrt(to[now^1],siz[to[now^1]]);
if(rt1 != -1) Solve(rt1,dep+1);
else Dep[to[now^1]] = dep;
}
int main(){
scanf("%d%d%d",&n,&q,&A);
for(int i=1;i<=n;i++) scanf("%d",&x[i]);
for(int i=1;i<n;i++){
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
Node(u,v,c),Node(v,u,c);
}
//printf("%d\n",findrt(1,n));
Solve(findrt(1,n),0);
LL ans = 0;
for(int u,a,b,L,R;q--;){
scanf("%d%d%d",&u,&a,&b);
L=min((a+ans)%A,(b+ans)%A),R=max((a+ans)%A,(b+ans)%A);
ans = 0;
for(int i=Dep[u];i>=0;i--){
int l = L , r = R , now = fa[u][i] , sd = sid[u][i];
l = lower_bound(vec[now][sd^1].begin(),vec[now][sd^1].end(),make_pair(l,-1ll))-vec[now][sd^1].begin()-1;
r = upper_bound(vec[now][sd^1].begin(),vec[now][sd^1].end(),make_pair(r,0x3f3f3f3f3f3f3f3fll))-vec[now][sd^1].begin()-1;
//printf("%d %d %d\n",l,r,vec[now][sd^1].size());
ans += (r-l) * 1ll * (fadis[u][i] + cst[now]) + ((r>=0?vec[now][sd^1][r].second:0) - (l>=0?vec[now][sd^1][l].second:0));
//printf("%d %lld\n",i,ans);
}
printf("%lld\n",ans);
}
}