DAY5测试T2 Tree(LCA+线段树/树上差分)

【问题描述】
小s是A星球的霸主。A星球上有n个城市,城市之间有一些道路。小s发现城市和道路刚好组成了一棵树。并且他发现如果有任意一条道路损坏,城市和城市之间就不连通了。A星球上还有m对工作站(A, B)。每对工作站在工作的时候,需要人员从A城市走到B城市。
现在A星球有Q个任务,每个任务可以由第L对工作站到第R对工作站任意一个来完成。小s想知道对于每个任务,损坏了哪些边会导致任务不能完成。他想把这个问题交给你,但你只需要告诉他损坏的边的总长度即可。
【输入格式】
输入的第一行包含1个整数n,表示城市个数。
接下来n-1行,每行包含3个整数x, y, z,表示边(x, y)和边的长度z。
接下来一行一个整数m,表示m对工作站。
接下来m行,每行包含两个整数A, B
接下来一行一个整数Q,表示任务数。
接下来Q行,每行两个整数L,R。表示第i个任务可以由第L对到第R对工作站完成。
【输出格式】
输出Q行,第i行表示第i个任务的答案。
【样例输入】
4
1 2 5
2 3 2
1 4 3
2
1 2
3 4
1
1 2
【样例输出】
5
【数据范围与约定】
30%: n, m, Q <= 200
60%: n, m, Q <= 1000
100%: n, m, Q <= 50000


一看到题就想起之前做的NOIP2015 运输计划了。简化题意为求第L~R条给定路径公共边的权值和。那么跑一遍dfs预处理出 dis[ i ] 数组表示节点 i 到根节点的距离,则可得每对工作站的路径长。
然后开始读入查询,对于每对查询都做一遍差分。差分数组为 dif 。例如对于某对工作站 ( x, y ),dif [ x ]+=1, dif [ y ]+=1 ,dif[ LCA( x, y ) ]-=2。处理完 [ L,R ] 后求一遍子树权值和。若某个点权值和等于 R-L+1 ,则其到父节点的那条边必为公共边。由于倍增求LCA中会处理出数组 f[ x][ 0 ],故易得其父节点。枚举 n 求和即可。
时间复杂度大致是 O( nq ),能过六十分。但是要开long long。以后这种累加的都应该注意一点。

正解很绝妙。
通过观察可以发现对于两条路径:(x1,y1)和(x2,y2),在LCA(x1,x2),LCA(x1,y2),LCA(y1,x2),LCA(y1,y2)中取深度最大的两个点,这两点之间的路径即为公共路径。画个图就能明白。
这么说题目暗示还是给的很明显的,“第L对到第R对”,还有这么大的查询量,肯定要用数据结构维护了。我们考虑线段树。
用线段树维护 1~m 对工作站。每段区间(x,y)都表示第 x 对~第 y 对按上述方法求出的公共路径的两个端点。剩下都是线段树的常规操作。

调了很久……居然是因为忘记删一个比较微妙的打表。

#include<bits/stdc++.h>
using namespace std;

const int maxn=50001;

int n,m,q;
int x,y,z;
int Link[maxn];
struct edge{
    int y,next,v;
}e[maxn<<1];
int Tot;

int dep[maxn];
long long dis[maxn];
int f[maxn][30];
bool vis[maxn];
int K;

struct Node{
    int N1,N2;
}Tree[maxn<<2],Temp2,Res;
int L,R;
int Temp1[4];

inline void read(int &x)
{
    x=0;int f=1;char s=getchar();
    for(;s<'0'||s>'9';s=getchar()) if(s=='-') f=-1;
    for(;s>='0'&&s<='9';s=getchar()) x=(x<<3)+(x<<1)+s-48;
    x*=f;
} 

inline void Insert(int x,int y,int v)
{
    e[++Tot].y=y;e[Tot].v=v;
    e[Tot].next=Link[x];Link[x]=Tot;
}

inline bool mycmp(int a,int b)
{
    return(dep[a]>dep[b]);
}

int get_LCA(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=K;i>=0;--i)
        if(dep[x]-(1<<i)>=dep[y]) x=f[x][i];
    if(x==y) return x;
    for(int i=K;i>=0;--i)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}

inline Node Unite(Node a,Node b)
{

    Temp1[0]=get_LCA(a.N1,b.N1);Temp1[1]=get_LCA(a.N1,b.N2);
    Temp1[2]=get_LCA(a.N2,b.N1);Temp1[3]=get_LCA(a.N2,b.N2);
    sort(Temp1,Temp1+4,mycmp);
    Temp2.N1=Temp1[0];Temp2.N2=Temp1[1];
    return Temp2;
}//合并。

void dfs_mul(int x)
{
    vis[x]=1;
    for(int i=1;(1<<i)<=dep[x];++i) f[x][i]=f[f[x][i-1]][i-1];
    for(int i=Link[x];i;i=e[i].next)
        if(!vis[e[i].y]){
            dep[e[i].y]=dep[x]+1;dis[e[i].y]=dis[x]+1LL*e[i].v;f[e[i].y][0]=x;
            dfs_mul(e[i].y);
        }
}

void Build(int Root,int l,int r)
{
    if(l==r){
        read(Tree[Root].N1);read(Tree[Root].N2);
        return;
    }
    int Mid=(l+r)>>1;
    Build(Root<<1,l,Mid);Build(Root<<1|1 ,Mid+1,r);
    Tree[Root]=Unite(Tree[Root<<1],Tree[Root<<1|1]);
}

Node Query(int Root,int l,int r)
{
    if(L<=l&&r<=R) return Tree[Root];
    int Mid=(l+r)>>1;
    if(L>Mid) return Query(Root<<1|1,Mid+1,r);
    else if(R<=Mid) return Query(Root<<1,l,Mid);
    else return Unite(Query(Root<<1,l,Mid),Query(Root<<1|1,Mid+1,r)); 
}

void Init()
{
    read(n);
    Tot=0;
    for(int i=1;i<n;++i) 
    {
        read(x);read(y);read(z);
        Insert(x,y,z);Insert(y,x,z); 
    }
}

void Prepare()
{
    memset(vis,0,sizeof(vis));
    memset(dep,0,sizeof(dep));
    memset(f,0,sizeof(f));
    memset(dis,0,sizeof(dis));
    dep[1]=1;f[1][0]=-1;
    dfs_mul(1);
    K=(int)(log(n)/log(2))+1;
    read(m);
    Build(1,1,m);
}

void Work()
{
    read(q);
    for(int i=1;i<=q;++i)
    {
        read(L);read(R);
        Res=Query(1,1,m); 
        printf("%lld\n",1LL*dis[Res.N1]+1LL*dis[Res.N2]-1LL*(dis[get_LCA(Res.N1,Res.N2)]<<1));
    }
}

int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    Init();
    Prepare();
    Work();
    fclose(stdin);fclose(stdout);
    return 0;
}

这几天唯一一道确定能拿到部分分的题,居然因为 long long 的问题挂掉了。非常不应该。太久了都没有概念了啊。

猜你喜欢

转载自blog.csdn.net/hfl030/article/details/80979762
今日推荐