Atcoder USACO 2019 December Contest C Milk Visit LCA变式

Milk Visits

题目大意:

有n个农场,这些农场由n-1条边相连【构成一棵树】,每个节点都会有一个权值 G或H,M次询问,每次输入x,y,ch,输出从x到y过程中能否遇见权值为ch的农场

 

题目思路:

没做过多少LCA的应用,至于这个题也没有看出来,其实LCA的作用在于求树上两点的距离,可以运用LCA求出树上两点最近路径的权值之和。

所以这个题考察的就是LCA求两点路径权值之和,只不过点权变为了边权,首先我们需要把问题转换一下,我们令G为1,H为-1,那么问题就转换成为了,如果两点之间路径点权之和==(路径长度+1),代表这条路全为G,路径点权之和==(路径长度+1)的相反数,代表这条路全为H,在这两者之间的话既有G又有H。所以只需要在LCA的时候不仅维护deep数组,再维护一个点权数组就可以了。

最后判断一下,路径的点权和与路径上点数的关系,就可以得到答案。

 

AC:

#include 
using namespace std;
typedef long long ll;
const ll INF=1000000000000005;
const ll maxn=5e5+5;
const int mod=998244353;
ll n,m;
int deep[maxn];//树的深度
int f[maxn][30];//fa[i][j] 节点i的2^j次方的祖先
struct node{
    int e,next;
}edge[maxn];
ll cnt=0;
int head[maxn];
int dis[maxn];
char str[maxn];
void addedge(int u,int v)
{
    edge[cnt]=node{v,head[u]};
    head[u]=cnt++;
}
void dfs(int u,int fa,int z)//预处理深度 与祖先的关系
{
    deep[u]=deep[fa]+1;
    dis[u]=dis[fa]+z;
    f[u][0]=fa;
    for(int i=1;(1<=0;i--)   if(deep[f[u][i]]>=deep[v]) u=f[u][i];//比他深往上跳
    if(u==v) return u;
    for(int i=25;i>=0;i--){
        if(f[u][i]!=f[v][i]){//因为从同一深度开始向上跳 一样有可能是更远的祖先
            u=f[u][i];
            v=f[v][i];
        }
    }
    return f[u][0];//随便返回一个上一级即可
}
int main()
{
    memset(head,-1,sizeof(head));
    scanf("%lld%lld",&n,&m);
    scanf("%s",str+1);
    for(int i=1;i<=n-1;i++)
    {
        ll x,y;scanf("%lld%lld",&x,&y);
        addedge(x,y);
        addedge(y,x);
    }
    int tempx=(str[1]=='G'?1:-1);
    dfs(1,1,tempx);
    for(int i=1;i<=m;i++)
    {
        ll x,y;
        char op[5];
        scanf("%lld%lld%s",&x,&y,op);
        int amit=LCA(x,y);
        int dis1=deep[x]+deep[y]-2*deep[amit]+1;
        int dis2=dis[x]+dis[y]-2*dis[amit]+(str[amit]=='G'?1:-1);
        if(dis2>-dis1&&dis2

 

你可能感兴趣的:(倍增,树上倍增)