天天爱跑♂步

线段树,枚举,暴力

写的头皮发麻......

对于每一次跑动(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;
}

总结:

遇到神仙题目时一定要先读题,找到信息,然后考虑难点在于什么,可不可以用数据结构维护,简化。

posted @ 2018-09-18 15:45  Splitor  阅读(187)  评论(0编辑  收藏  举报