题目大意:给定一棵无根树和一个序列,在这个序列上依次遍历,求每个点的访问次数(最后一个点的访问次数要-1)
树链剖分的裸题……考场上我还是一个弱渣,啥也不会,暴力得了50分,剩下两道题爆零了。。。而且30W深搜爆栈,人生第一次手写了系统栈。。
回来因为这题的原因去学了树链剖分 结果没学明白 每条重链单独开了一棵线段树 常数大的要死
高一时写的代码。。。还是别看了,拿去对拍可以,阅读性欠佳
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define M 300100
#define min(x,y) x<y?x:y
typedef struct abcd{int xx,mark,l,r;abcd*ls,*rs;} abcd;abcd*tree[M];
typedef struct abcde{int to;abcde*next;} abcde;abcde*head[M];
void add(int x,int y){abcde*temp=(abcde*)malloc(sizeof(abcde));temp->to=y;temp->next=head[x];head[x]=temp;}
void swap(int &x,int &y){int z=x;x=y;y=z;}
int fa[M],siz[M],dpt[M],son[M],top[M],a[M],ans;
void Build_tree(abcd*p,int l,int r)
{
int mid=l+r>>1;
p->l=l;p->r=r;p->xx=p->mark=0;p->ls=p->rs=NULL;
if(l==r)return ;
p->ls=(abcd*)malloc(sizeof(abcd));
p->rs=(abcd*)malloc(sizeof(abcd));
Build_tree(p->ls,l,mid);
Build_tree(p->rs,mid+1,r);
}
void Up_load(abcd*p,int x,int y)
{
int mid=p->l+p->r>>1;
if(p->l==x&&p->r==y){p->xx+=p->r-p->l+1;p->mark++;return ;}
if(y<=mid)Up_load(p->ls,x,y);
else if(x>mid)Up_load(p->rs,x,y);
else Up_load(p->ls,x,mid),Up_load(p->rs,mid+1,y);
p->xx+=y-x+1;
}
int search(abcd*p,int x)
{
int mid=p->l+p->r>>1;
if(p->l==x&&p->r==x)return p->xx;
p->ls->xx+=p->mark*(p->ls->r-p->ls->l+1);
p->rs->xx+=p->mark*(p->rs->r-p->rs->l+1);
p->ls->mark+=p->mark;
p->rs->mark+=p->mark;
p->mark=0;
if(x<=mid)return search(p->ls,x);return search(p->rs,x);
}
int q[M],r,h;
int maxsiz[M];
void bfs()
{
abcde*temp;
int x;
q[++h]=1;
while(r!=h)
{
r++;
x=q[r];
dpt[x]=dpt[fa[x]]+1;
siz[x]=1;
for(temp=head[x];temp;temp=temp->next)
{
if(temp->to==fa[x])continue;
fa[temp->to]=x;
q[++h]=temp->to;
}
}
for(;r>=2;r--)
{
x=q[r];
siz[fa[x]]+=siz[x];
if(siz[x]>maxsiz[fa[x]])maxsiz[fa[x]]=siz[x],son[fa[x]]=x;
}
for(r=1;r<=h;r++)
{
x=q[r];
if (son[fa[x]]!=x)top[x]=x,tree[x]=(abcd*)malloc(sizeof(abcd));
else top[x]=top[fa[x]];
if(son[x]==0)Build_tree(tree[top[x]],1,dpt[x]-dpt[top[x]]+1);
}
}
int n;
int main()
{
int i,x,y,f1,f2;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
bfs();
for(i=1;i<n;i++)
{
x=a[i];y=a[i+1];
f1=top[x];f2=top[y];
while(f1!=f2)
{
if(dpt[f1]<dpt[f2])swap(x,y),swap(f1,f2);
Up_load(tree[f1],1,dpt[x]-dpt[f1]+1);
x=fa[f1];
f1=top[x];
}
if(dpt[x]<dpt[y])swap(x,y);
Up_load(tree[f1],dpt[y]-dpt[f1]+1,dpt[x]-dpt[f1]+1);
}
for(i=1;i<=n;i++)
printf("%d\n",search(tree[top[i]],dpt[i]-dpt[top[i]]+1)-1+(i==a[1]));
}
后来经过讨论其实这题LCA就能过。。。每次移动时在起始点和终点各打一个start标记,在LCA和LCA的父节点处各上打一个end标记,然后深搜,start标记一直上传,遇到end标记就停止,最后再处理一下就行
我写的是TarjanLCA 这样询问深搜一遍处理 时间复杂度O(n)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define M 300100
struct abcd{
int to,next;
}table[M<<2];
int head[M],query[M],tot;
void add(int x,int y)
{
table[++tot].to=y;
table[tot].next=head[x];
head[x]=tot;
}
void qadd(int x,int y)
{
table[++tot].to=y;
table[tot].next=query[x];
query[x]=tot;
}
int fa[M],f[M],a[M],v[M];
int start[M],end[M];
int n;
int find(int x)
{
if(!f[x]||f[x]==x)
return f[x]=x;
return f[x]=find(f[x]);
}
void Tarjan(int x)
{
int i;
for(i=head[x];i;i=table[i].next)
if(table[i].to!=fa[x])
fa[table[i].to]=x,Tarjan(table[i].to),f[table[i].to]=x;
v[x]=1;
for(i=query[x];i;i=table[i].next)
if(v[table[i].to])
end[ fa[ find(table[i].to) ] ]++,end[ find(table[i].to) ]++;
start[x]-=end[x];
start[fa[x]]+=start[x];
}
int main()
{
int i,x,y;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(i^1)
{
qadd(a[i],a[i-1]);
qadd(a[i-1],a[i]);
start[ a[i] ]++;
start[ a[i-1] ]++;
}
}
for(i=1;i<n;i++)
scanf("%d%d",&x,&y),add(x,y),add(y,x);
Tarjan(1);
for(i=1;i<=n;i++)
printf("%d\n", start[i] - (i!=a[1]) );
}