BZOJ 3631 JLOI2014 松鼠的新家 树链剖分/LCA

题目大意:给定一棵无根树和一个序列,在这个序列上依次遍历,求每个点的访问次数(最后一个点的访问次数要-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]) );
}


你可能感兴趣的:(LCA,Tarjan,树链剖分,bzoj,BZOJ3631)