Split The Tree(dfs序加统计区间不同数的个数)

题目描述

For their physical fitness program, N (2 ≤ N ≤ 1,000,000) cows have decided to run a relay race using the T (2 ≤ T ≤ 100) cow trails throughout the pasture.
Each trail connects two different intersections (1 ≤ I1i ≤ 1,000; 1 ≤ I2i ≤ 1,000), each of which is the termination for at least two trails. The cows know the lengthi of each trail (1 ≤ lengthi  ≤ 1,000), the two intersections the trail connects, and they know that no two intersections are directly connected by two different trails. The trails form a structure known mathematically as a graph.
To run the relay, the N cows position themselves at various intersections (some intersections might have more than one cow). They must position themselves properly so that they can hand off the baton cow-by-cow and end up at the proper finishing place.
Write a program to help position the cows. Find the shortest path that connects the starting intersection (S) and the ending intersection (E) and traverses exactly N cow trails.

输入

* Line 1: Four space-separated integers: N, T, S, and E
* Lines 2..T+1: Line i+1 describes trail i with three space-separated integers: lengthi , I1i , and I2i

输出

* Line 1: A single integer that is the shortest distance from intersection S to intersection E that traverses exactly N cow trails.

样例输入

2 6 6 4
11 4 6
4 4 8
8 4 9
6 6 8
2 6 9
3 8 9

样例输出

10

 所谓dfs序,就是把一棵树根据遍历的顺序压进一个序列里,这样我们就可以用线段树树状数组等各种骚操作搞它了。

我们dfs后 ,一棵树变成了一段序列。

然后对于每个节点,它的子树一定是一段连续的区间,那么问题就转化成了 给定一段区间,求区间不同数的个数加上剩余数不同数的个数,我们把序列延长一倍,就变成了统计两段连续区间内不同数的个数。树状数组瞎搞即可。

#include <bits/stdc++.h>
#define maxn 200005
using namespace std;
struct edges
{
    int u,v,next;
}edge[maxn];
int head[maxn];
int cnt=0;
int n;
void addedge(int u,int v)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
int sizes[maxn];
int in[maxn];//in表示遍历的时间戳
map<int,int> Mp;
int out[maxn];//出来的时间戳,那么子树大小(包括本身)就是out[i]-in[i]
bool vis[maxn];
int x=1;
void dfs(int num)//dfs序
{
    for(int i=head[num];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(!vis[v])
        {
            vis[v]=true;
            in[v]=x++;
            Mp[x-1]=v;
            dfs(v);
            out[v]=x;
        }
    }
}
int ans[maxn];
int tree[maxn];
int lowbit(int x)
{
    return x&(-x);
}
void add(int pos,int val)
{
    for(int i=pos;i<=n;i+=lowbit(i))
    {
        tree[i]+=val;
    }
}
int sum(int x)
{
    int res=0;
    for(int i=x;i>0;i-=lowbit(i))
    {
        res+=tree[i];
    }
    return res;
}
struct quer
{
    int l,r,id;
}query[maxn*2];
bool cmp(quer a,quer b)
{
    return a.r<b.r;
}
int main()
{
    int i;
    //freopen("in.txt","r",stdin);
    scanf("%d",&n);
    memset(head,-1,sizeof(head));
    for(int i=2;i<=n;i++)
    {   int v;
        scanf("%d",&v);
        addedge(i,v);
        addedge(v,i);
    }
    vis[1]=true;
    in[1]=x++;
    Mp[1]=1;
    dfs(1);
    out[1]=x;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&sizes[in[i]]);
    }
    for(int i=n+1;i<=2*n;i++)
    {
        sizes[i]=sizes[i-n];
    }
    n*=2;
    int index=0;
    for(int i=2;i<=n/2;i++)//这一步是在枚举每一棵树的子树,把它加到询问里。
    {
        int v=Mp[i];
        int l=i;
        int r=out[v]-1;
        query[index].id=index;
        query[index].l=l;
        query[index++].r=r;
        query[index].id=index;
        query[index].l=r+1;
        int tmp=n/2-(r-l+1);
        query[index].r=query[index].l+tmp-1;
        index++;
    }
    sort(query,query+index,cmp);
    int  cur=1;
    Mp.clear();
    for(int i=0;i<index;i++)//树状数组离线搞
    {
        for(int j=cur;j<=query[i].r;j++)
        {
            if(Mp[sizes[j]]!=0)
            {
                add(Mp[sizes[j]],-1);
            }
            add(j,1);
            Mp[sizes[j]]=j;
        }
        cur=query[i].r+1;
        ans[query[i].id]=sum(query[i].r)-sum(query[i].l-1);
    }
    int res=0;
    for(int i=0;i<index;i+=2)
    {
        res=max(res,ans[i]+ans[i+1]);
    }
    printf("%d
",res);
    return 0;
}

  

原文地址:https://www.cnblogs.com/zyf3855923/p/9596980.html