天天爱跑♂步
线段树,枚举,暴力
写的头皮发麻......
对于每一次跑动(u,v)显然,我们需要对(u,lca),(lca,v)分别处理。为什么?考虑每个点 i 能得到的贡献,一种是从以它为根的子树跑上来,另一种是路径的终点在它的子树中。第一类:d[s]-d[i]=w[i],得到式子:d[s]=d[i]+w[i]。第二类:T[s,t]=w[i]+d[t]-d[i],得到:T[s,t]-d[t]=w[i]-d[i]。我们需要进行树上区间修改,单点查询,而且还要分深度进行(例如,深度为k的起点能贡献到的点的权值++)所以我们选择按深度建立N*2棵线段树,每个深度有两个线段树,分别维护那两个值。然后就写吧:树剖+lca+线段树+动态开点。
错误集锦
1、动态开点线段树记得pushdown时要开好点。
2、记得去掉对lca的重复贡献。
3、线段树的点要开的足够多!!
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define half (l+r)>>1
using namespace std;
const int maxn=600008;
int head[maxn],cur;
int n,m,tot[maxn],top[maxn],fa[maxn],d[maxn],son[maxn],id[maxn],fina[maxn];
int tre,num[maxn],wh[maxn];
int wow[maxn];
struct hzw
{
int to,next;
}e[maxn];
struct ljm
{
int w;
int lc,rc,tag;
}t[10000006][2];
inline int read(){
int out=0,flag=1;char c=getchar();
while(c<48||c>57) {if(c=='-') flag=-1;c=getchar();}
while(c>=48&&c<=57){out=out*10+c-48;c=getchar();}
return out*flag;
}
inline void add(int a,int b)
{
e[cur].to=b;
e[cur].next=head[a];
head[a]=cur++;
}
inline void pushdown (int u,int l,int r,int k){
int mid=half;
if (!t[u][k].lc) t[u][k].lc=++tre;
if (!t[u][k].rc) t[u][k].rc=++tre;
t[t[u][k].lc][k].w+=t[u][k].tag*(mid-l+1);
t[t[u][k].lc][k].tag+=t[u][k].tag;
t[t[u][k].rc][k].w+=t[u][k].tag*(r-mid);
t[t[u][k].rc][k].tag+=t[u][k].tag;
t[u][k].tag=0;
}
inline void update(int &s,int l,int r,int ll,int rr,int v,int k)
{
if (!s) s=++tre;
if (l==ll&&r==rr)
{
t[s][k].w+=(r-l+1)*v;
t[s][k].tag+=v;
return;
}
if (t[s][k].tag) pushdown(s,l,r,k);
int mid=half;
if (rr<=mid) update(t[s][k].lc,l,mid,ll,rr,v,k);
else if (ll>mid) update(t[s][k].rc,mid+1,r,ll,rr,v,k);
else
{
update(t[s][k].lc,l,mid,ll,mid,v,k);
update(t[s][k].rc,mid+1,r,mid+1,rr,v,k);
}
t[s][k].w=t[t[s][k].lc][k].w+t[t[s][k].rc][k].w;
}
inline int query(int s,int l,int r,int cl,int cr,int k)
{
if (!s) return 0;
if (l==cl&&r==cr)
{
return t[s][k].w;
}
if (t[s][k].tag) pushdown(s,l,r,k);
int mid=half;
if (cr<=mid) return query(t[s][k].lc,l,mid,cl,cr,k);
else if (cl>mid) return query(t[s][k].rc,mid+1,r,cl,cr,k);
else {
return query(t[s][k].lc,l,mid,cl,mid,k)+query(t[s][k].rc,mid+1,r,mid+1,cr,k);
}
}
inline int dfs1(int s,int f,int l)
{
d[s]=l;
fa[s]=f;
tot[s]=1;
int maxs=-23333;
for (int i=head[s];i!=-1;i=e[i].next)
{
if (e[i].to==f) continue;
tot[s]+=dfs1(e[i].to,s,l+1);
if (tot[e[i].to]>maxs)
{
maxs=tot[e[i].to];
son[s]=e[i].to;
}
}
return tot[s];
}
int cnt=0;
inline void dfs2(int s,int firs)
{
id[s]=++cnt;
top[s]=firs;
if (!son[s]) return;
dfs2(son[s],firs);
for (int i=head[s];i!=-1;i=e[i].next)
{
if (id[e[i].to]) continue;
dfs2(e[i].to,e[i].to);
}
}
inline int lca(int x,int y)
{
if (x==y) return x;
while (top[x]!=top[y])
{
if (d[top[x]]<d[top[y]]) swap(x,y);
x=fa[top[x]];
}
if (d[x]>d[y]) swap(x,y);
return x;
}
inline void treup(int root,int a,int b,int val,int k)
{
while (top[a]!=top[b])
{
int shit=n+root;
if (d[top[a]]<d[top[b]]) swap(a,b);
update(shit,1,n,id[top[a]],id[a],val,k);
a=fa[top[a]];
}
if (d[a]>d[b]) swap(a,b);
int shit=n+root;
update(shit,1,n,id[a],id[b],val,k);
}
int main() {
cin>>n>>m;
tre=3*n;
memset(head,-1,sizeof (head));
for (int i=1,a,b;i<=n-1; ++i) {
a=read(),b=read();
add(a,b);
add(b,a);
}
for (int i=1;i<=n;++i) scanf("%d",&wh[i]);
dfs1(1,1,1);
dfs2(1,1);
int x,y;
for (int i=1; i<=m; ++i) {
x=read(),y=read();
int tmp=lca(x,y);
int tim=d[x]-d[tmp]+d[y]-d[tmp];
treup(d[x],x,tmp,1,0);
treup(tim-d[y],tmp,y,1,1);
if (d[tmp]+wh[tmp]==d[x]&&wh[tmp]-d[tmp]==tim-d[y]) wow[tmp]++;
}
for (int i=1;i<=n;++i)
{
fina[i]+=query(n+d[i]+wh[i],1,n,id[i],id[i],0);
fina[i]+=query(wh[i]-d[i]+n,1,n,id[i],id[i],1);
fina[i]-=wow[i];
}
for (int i=1;i<=n;++i)
{
printf("%d ",fina[i]);
}
return 0;
}
总结:
遇到神仙题目时一定要先读题,找到信息,然后考虑难点在于什么,可不可以用数据结构维护,简化。