1. 程式人生 > >2018東北四省賽 Split The Tree(區間種數的求法)

2018東北四省賽 Split The Tree(區間種數的求法)

7229: Split The Tree

時間限制: 1 Sec  記憶體限制: 128 MB
提交: 68  解決: 18
[提交] [狀態] [討論版] [命題人:admin]

題目描述

You are given a tree with n vertices, numbered from 1 to n. ith vertex has a value wi
We define the weight of a tree as the number of different vertex value in the tree.
If we delete one edge in the tree, the tree will split into two trees. The score is the sum of these two trees’ weights.
We want the know the maximal score we can get if we delete the edge optimally.

輸入

Input is given from Standard Input in the following format:
n
p2 p3  . . . pn
w1 w2  . . . wn
Constraints
2 ≤ n ≤ 100000 ,1 ≤ pi < i
1 ≤ wi ≤ 100000(1 ≤ i ≤ n), and they are integers
pi means there is a edge between pi and i

輸出

Print one number denotes the maximal score.

樣例輸入

3
1 1
1 2 2

樣例輸出

3

來源/分類

[提交] [狀態]

【題意】

給出一棵樹,n個結點,每個結點有一個權值w[i],現在我可以刪掉一個邊,使得樹分成兩部分。每一部分的得分等於本部分中權值w[i]的不同的個數。總分=兩部分的分數之和,求最大總分。

【分析】

求一下這棵樹的dfs序,為每個點設定時間戳rk[i],發現,對於任意子樹,其所有點的rk[i]必定是一段連續的時間戳。用dfs序把樹轉化成序列,我們刪掉一個邊,就相當於拿下來一個子樹(連續時間戳),和剩下的部分,算個分數和。子樹好算,就是一段連續區間內數字的種數。剩下的部分沒有連續時間戳,怎麼辦?把整個時間戳序列1~n複製到n+1~2n,會發現,不連續的那部分,可以用複製部分連線起來,從而變得連續。

剩下的問題,就變成列舉刪邊,也就是列舉子樹所對應的時間戳區間,求區間數字種數。

【dfs序】

按照dfs的遍歷順序,為每個點標記一個到達的時間,開始於1;然後根據時間戳把樹轉化成線性序列。

【區間種數】

求一個數列,某區間內的數字種數。

有3種方法:1.莫隊演算法。2.樹狀陣列。.3主席樹。

莫隊演算法:求每個區間的種數,莫隊的鐵飯碗。只不過時間複雜度略高O(n*sqrt(n));

樹狀陣列:對於一個數列,每個數字第一次出現的位置標記為1,然後以1為左端點的區間的種數就是1->R之間標記的個數,也就是字首和,因此用樹狀陣列維護。若區間左端點為2呢?我們一步一步走,剛才我們在1處,現在我們要到2處,故1處要丟掉,也就是把標記清為0,並且,把1處數字下一次出現的位置標記為1,好,現在就能直接詢問2->R的區間種數了,即R的字首和了。 該做法的巧妙之處在於只標記每個數字以L為起點時第一次出現的位置,故達到去重目的。

主席樹:待實現。。。

【程式碼--莫隊】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX=1e6+5;
struct node{
    int t,next;
}edge[MAX];
int head[MAX],cnt;
void init(int n)
{
    cnt=0;
    memset(head,-1,sizeof(head[0])*++n);
}
void addedge(int u,int v)
{
    edge[cnt]=node{v,head[u]};
    head[u]=cnt++;
}
 
struct query{
    int L,R,id;
}Q[MAX];
int block;
bool cmpblock(query a,query b)
{
    if(a.L/block == b.L/block)return a.R<b.R;
    return a.L/block < b.L/block;
}
 
int n,p,w[MAX];
int rk[MAX],ri[MAX];
int id[MAX];
 
void dfs(int u,int &tag)
{
    rk[u]=++tag;
    id[tag]=u; //tag時間戳,對應點u
    for(int i=head[u];~i;i=edge[i].next)
        dfs(edge[i].t,tag);
    ri[u]=tag;
}
 
int eachin[MAX],eachout[MAX];
void update(int pos,int change,int &in,int &out)
{
    int val=w[id[pos]];
    eachin[val]+=change;
    if(eachin[val]==1&&change==1 || eachin[val]==0&&change==-1)
        in+=change;
    change*=-1;
    eachout[val]+=change;
    if(eachout[val]==0&&change==-1 || eachout[val]==1&&change==1)
        out+=change;
}
int main()
{
    scanf("%d",&n);
    init(n);
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&p);
        addedge(p,i);
    }
    for(int i=0;i<=100000;i++)
        eachin[i]=eachout[i]=0;
    set<int>S;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
        S.insert(w[i]);
        eachout[w[i]]++;
    }
    int tag=0;
    dfs(1,tag);
    for(int i=1;i<=n;i++)
        Q[i]=query{rk[i],ri[i],i};
    block=pow(n,0.57);
    sort(Q+1,Q+1+n,cmpblock);
    int l=1,r=0, in=0,out=S.size(),ans=0;
    for(int i=1;i<=n;i++)
    {
        int L=Q[i].L, R=Q[i].R;
        for(;l<L;l++) update(l,-1,in,out);
        for(;l>L;l--) update(l-1,1,in,out);
        for(;r<R;r++) update(r+1,1,in,out);
        for(;r>R;r--) update(r,-1,in,out);
        ans=max(ans,in+out);
    }
    printf("%d\n",ans);
    return 0;
}

【程式碼--樹狀陣列】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX=1e6+5;
struct node{
    int t,next;
}edge[MAX];
int head[MAX],cnt;
void init(int n)
{
    cnt=0;
    memset(head,-1,sizeof(head[0])*++n);
}
void addedge(int u,int v)
{
    edge[cnt]=node{v,head[u]};
    head[u]=cnt++;
}

int btree[MAX];
void add(int k,int num,int top)
{
    for(;k<=top;k+=k&-k)
        btree[k]+=num;
}
int Qsum(int k)
{
    int res=0;
    for(;k;k-=k&-k)
        res+=btree[k];
    return res;
}

struct query{
    int L,R,id;
}Q[MAX];
bool cmp(query a,query b)
{
    if(a.L==b.L)return a.R<b.R;
    return a.L<b.L;
}
int n,p,w[MAX];
int rk[MAX],ri[MAX];
int id[MAX];
int last[MAX],nxt[MAX];
int ans[MAX];

void dfs(int u,int &tag)
{
    rk[u]=++tag;
    id[tag]=u; //tag時間戳,對應點u
    for(int i=head[u];~i;i=edge[i].next)
        dfs(edge[i].t,tag);
    ri[u]=tag;
}
int main()
{
    scanf("%d",&n);
    init(n);
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&p);
        addedge(p,i);
    }
    for(int i=0;i<=100000;i++)last[i]=0;
    for(int i=0;i<=n+n;i++)btree[i]=0,nxt[i]=0;
    for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    int tag=0;
    dfs(1,tag);
    for(int i=1;i<n+n;i++)
    {
        int val=w[id[i>n?i-n:i]];
        if(last[val]==0)
            add(i,1,n+n);
        else
            nxt[last[val]]=i;
        last[val]=i;
    }
    for(int i=1;i<=n;i++)
    {
        Q[i]=query{rk[i],ri[i],i};
        Q[i+n]=query{ri[i]+1,rk[i]+n-1,i};
    }
    sort(Q+1,Q+1+n+n,cmp);
    for(int i=1;i<=n;i++)ans[i]=0;
    int L=1;
    for(int i=1;i<=n+n;i++)
    {
        for(;L<Q[i].L;L++)
        {
            add(L,-1,n+n);
            if(nxt[L])
                add(nxt[L],1,n+n);
        }
        ans[Q[i].id]+=Qsum(Q[i].R);
    }
    for(int i=2;i<=n;i++)ans[i]=max(ans[i],ans[i-1]);
    printf("%d\n",ans[n]);
    return 0;
}