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]) );
}
时间: 2024-08-15 06:40:41

BZOJ 3631 JLOI2014 松鼠的新家 树链剖分/LCA的相关文章

BZOJ 3631: [JLOI2014]松鼠的新家( 树链剖分 )

裸树链剖分... ------------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; const int maxn = 300009; struct edge { int to; edge* next; } E[maxn << 1], *pit = E, *head[maxn]; inline void add(int u,

BZOJ3631: [JLOI2014]松鼠的新家 树链剖分/lca/树上查分

求n次lca后树上差分. 每次移动时在起始点和终点各打一个start标记,在LCA和LCA的父节点处各上打一个end标记,然后深搜,start标记一直上传,遇到end标记就停止,最后再处理一下就行 % PoPoQQQ大爷 #include<bits/stdc++.h> #define rep(i,l,r) for(int i=l;i<=r;i++) #define N 310000 using namespace std; int head[N],dep[N],sz[N],son[N],

[JLOI2014]松鼠的新家-树链剖分

最开始的时候我在写线段树部分的时候还打了一个build,后来一想,打个球球大作战的build啊!!!有个锤子的用啊!!! 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn = 3e6+5; 4 int n; 5 int e,begin[maxn],next[maxn],to[maxn],a[maxn]; 6 int tree[maxn<<2],lazy[maxn<<2]; 7 int so

BZOJ3631 松鼠的新家(树链剖分)

题目链接 松鼠的新家 差不多可以说是树链剖分的模板题了,直接维护即可. 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define REP(i,n) for(int i(0); i < (n); ++i) 6 #define rep(i,a,b) for(int i(a); i <= (b); ++i) 7 #define dec(i,a,b) for(int i(a); i >= (b); --i) 8 #

BZOJ 3631 [JLOI2014]松鼠的新家 | 树上差分

链接 BZOJ 3631 题解 看起来是树剖?实际上树上差分就可以解决-- 当要给一条路径(u, v) +1的时候,给d[u] += 1, d[v] += 1, d[lca(u, v)] -= 1, d[fa[lca(u, v)]] -= 1. 注意这道题中路径的终点是不 +1的. #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <q

BZOJ 3631 [JLOI2014]松鼠的新家

题解:树上差分就可以了 论看懂题意的重要性 最后-1的时候处理错了,WA了几发 #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn=600009; const int Inf=1000000000; int n,m; int a[maxn]; int cntedge; int head[maxn]; int to[maxn<<1],

[JLOI2014]松鼠的新家 (树剖)

题目 P3258 [JLOI2014]松鼠的新家 解析 非常裸的一道树剖题 链上修改+单点查询的板子 记录一下所经过的点\(now[i]\),每次更新\(now[i-1]到now[i]\) 我们链上更新时上一次到的终点,是这一次一次更新的起点,又因为在\(a_n\)处可以不放糖,所以我们每次链上更新完成后,在这条链的终点位置处糖数\(-1\). 然后套板子直接做 代码 #include <bits/stdc++.h> using namespace std; const int N = 2e6

【BZOJ 3631】 [JLOI2014]松鼠的新家

3631: [JLOI2014]松鼠的新家 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 681 Solved: 329 [Submit][Status][Discuss] Description 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在"树"上.松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的

[填坑]树上差分 例题:[JLOI2014]松鼠的新家(LCA)

今天算是把LCA这个坑填上了一点点,又复习(其实是预习)了一下树上差分.其实普通的差分我还是会的,树上的嘛,也是懂原理的就是没怎么打过. 我们先来把树上差分能做到的看一下: 1.找所有路径公共覆盖的边 例题:[NOIP2015]运输计划 (然而我还没过就先不讲了) 反正就是中间有一步要求一条边被所有计划公共覆盖. 那么怎么求它呢?暴力(滚粗).我们有一个非常好的方法就是树上差分(记录tmp为差分数组) 询问操作为从叶子节点的权值向上累加到root 在一条路径u→ v,如果tmp[u]++,那么我