Ahead
10.05.2018
把洛谷博客先搬过来 做法很多 这里就选树剖了正好当专题
一级毒瘤题
分析
树链+桶+神奇的差分+数学??(貌似没有什么不用的)
注释都在代码上了
很早之前的代码
为了干掉它花了一周似乎的说,记不住了
太早了懒得写太细的
需要了解更多滴滴我~~~
后面想想还是蛮简单的
也就后面想想了
代码
#include <bits/stdc++.h>
#define N 600001
using namespace std;
int n,m,root=1,w[N],ans[N];
vector <int> que[N]; //问题连接
vector <int> numq[N]; //问题序号
struct node //邻接表构树
{
int to,next;
}e[N]; int len,last[N];
struct LCAnode //LCA数据
{
int st,ed,lca;
}q[N]; int f[N]; bool vis[N];
void ins(int x,int y) { e[++len].to=y; e[len].next=last[x]; last[x]=len; } //加边
int tot[N],son[N],fa[N],dep[N];
void dfs1(int x) //记录树的数据
{
tot[x]=1;
for(int i=last[x];i;i=e[i].next) //访问亲朋好友
{
int y=e[i].to;
if(y!=fa[x])
{
fa[y]=x;
dep[y]=dep[x]+1;
dfs1(y);
son[x]=tot[son[x]]>tot[y]?son[x]:y; //找出重孙子
tot[x]+=tot[y];
}
}
}
int pos[N],Tm,top[N],rpos[N]; //找祖先,构建链子,dfs序
void dfs2(int x,int tp)
{
pos[x]=++Tm; top[x]=tp; rpos[Tm]=x;
if(!son[x]) return ;
dfs2(son[x],tp);
for(int i=last[x];i;i=e[i].next) //访问亲朋好友
{
int y=e[i].to;
if(y!=fa[x]&&y!=son[x])
dfs2(y,y);
}
}
int findfa(int x) { return f[x]==x?x:f[x]=findfa(f[x]); } //并查集找祖先(合并)
void Tarjan_LCA(int x) //公共祖先
{
f[x]=x; vis[x]=1;
int ii=que[x].size();
for(int i=0;i<ii;i++)
{
if(vis[que[x][i]]&&q[numq[x][i]].lca==0)
q[numq[x][i]].lca=findfa(que[x][i]);
}
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].to;
if(y!=fa[x])
{
Tarjan_LCA(y);
f[y]=x;
}
}
}
int s[N];
vector <int> st[N];
vector <int> ed[N];
void Swap(int &a,int &b) { int y=a; a=b; b=y; } //交换
void add(int x,int y,int fn) //树链跳跃
{
if(dep[x]<dep[y]) Swap(x,y); //调整高度
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) Swap(x,y);
st[pos[top[x]]].push_back(fn);
ed[pos[x]+1].push_back(fn);
x=fa[top[x]];
}
if(dep[x]<dep[y]) Swap(x,y);
st[pos[y]].push_back(fn);
ed[pos[x]+1].push_back(fn);
}
void cnt1() //先做ed到lca w[i]=(dep[s]-dep[lca])+(dep[i]-dep[lca])
{ //转换得 w[i]-dep[i]=dep[s]-2*dep[lca]
for(int i=1;i<=n;i++) w[i]-=dep[i];
for(int i=1;i<=m;i++) //依次处理每一个询问
{
//祖先点倘若成立的 先减一重复始点做的一次
if(w[q[i].lca]==dep[q[i].st]-2*dep[q[i].lca])
ans[q[i].lca]--;
add(q[i].lca,q[i].ed,dep[q[i].st]-2*dep[q[i].lca]); //将区间标号
}
for(int i=1;i<=n;i++) //依次枚举每一个观察者
{ //差分思想
for(int j=0;j<st[i].size();j++) s[st[i][j]]++; //以这个点作为起点的++
for(int j=0;j<ed[i].size();j++) s[ed[i][j]]--; //以这个点作为末点的--
ans[rpos[i]]+=s[w[rpos[i]]]; //满足当前的条件的点数
}
}
void cnt2() //st到lca w[i]=dep[st]-dep[i]
{ //转换得 w[i]+dep[i]=dep[st];
for(int i=0;i<=n;i++)
{
st[i].clear();
ed[i].clear();
}
memset(s,0,sizeof(s)); //初始化
for(int i=1;i<=n;i++) w[i]+=2*dep[i]; //上一次减得要加回来
for(int i=1;i<=m;i++) //依次处理每一个询问
add( q[i].lca , q[i].st , dep[q[i].st]);
for(int i=1;i<=n;i++)
{ //差分
for(int j=0;j<st[i].size();j++) s[st[i][j]]++;
for(int j=0;j<ed[i].size();j++) s[ed[i][j]]--;
ans[rpos[i]]+=s[w[rpos[i]]];
}
}
int main()
{
scanf("%d%d",&n,&m);
int x,y; //加边构树,管理权值
for(int i=1;i<n;i++){ scanf("%d%d",&x,&y); ins(x,y); ins(y,x); }
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=m;i++) //输入问题
{
scanf("%d%d",&x,&y);
que[x].push_back(y); que[y].push_back(x); //问题双向
numq[x].push_back(i); numq[y].push_back(i); //该问题的次序
q[i].st=x; q[i].ed=y;
}
dep[root]=1; dfs1(root); dfs2(root,root); //树链剖分
Tarjan_LCA(root);
cnt1(); cnt2(); //源点到祖先,祖先到汇点 ;
//输出
for(int i=1;i<n;i++)
printf("%d ",ans[i]);
printf("%d\n",ans[n]);
system("pause");
return 0;
}