3572: [Hnoi2014]世界树

5 篇文章 0 订阅

3572: [Hnoi2014]世界树

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 1385   Solved: 762
[ Submit][ Status][ Discuss]

Description

世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。
世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种族的编号分别从1到n,分别生活在编号为1到n的聚居地上,种族的编号与其聚居地的编号相同。有的聚居地之间有双向的道路相连,道路的长度为1。保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环。定义两个聚居地之间的距离为连接他们的道路的长度;例如,若聚居地a和b之间有道路,b和c之间有道路,因为每条道路长度为1而且又不可能出现环,所卧a与c之间的距离为2。
出于对公平的考虑,第i年,世界树的国王需要授权m[i]个种族的聚居地为临时议事处。对于某个种族x(x为种族的编号),如果距离该种族最近的临时议事处为y(y为议事处所在聚居地的编号),则种族x将接受y议事处的管辖(如果有多个临时议事处到该聚居地的距离一样,则y为其中编号最小的临时议事处)。
现在国王想知道,在q年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。 现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。

Input

第一行为一个正整数n,表示世界树中种族的个数。
接下来n-l行,每行两个正整数x,y,表示x聚居地与y聚居地之间有一条长度为1的双
向道路。接下来一行为一个正整数q,表示国王询问的年数。
接下来q块,每块两行:
第i块的第一行为1个正整数m[i],表示第i年授权的临时议事处的个数。
第i块的第二行为m[i]个正整数h[l]、h[2]、…、h[m[i]],表示被授权为临时议事处的聚居地编号(保证互不相同)。

Output

输出包含q行,第i行为m[i]个整数,该行的第j(j=1,2…,,m[i])个数表示第i年被授权的聚居地h[j]的临时议事处管理的种族个数。

Sample Input

10
2 1
3 2
4 3
5 4
6 1
7 3
8 3
9 4
10 1
5
2
6 1
5
2 7 3 6 9
1
8
4
8 7 10 3
5
2 9 3 5 8

Sample Output

1 9
3 1 4 1 1
10
1 1 3 5
4 1 3 1 1

HINT

N<=300000, q<=300000,m[1]+m[2]+…+m[q]<=300000

Source

[ Submit][ Status][ Discuss]



很显然是构建一棵虚树,然后在虚树上dp,但是细节挺多的。。。

每次询问,先将虚树构建出来,然后可以这样考虑

部落对答案的共线分配到点和链上,分开统计

比如,对于虚树的叶子节点,这个节点在原树中的子树的所有部落肯定都归它管了

还有是虚树的根节点,在原树中,处于这个根之外的所有节点先走到根,然后找一个议事处给它

其它节点也类似这样处理,也就是把不在虚树上的点当做点权

然后是虚树上的边,每条边的实质是原树的一条链,链上的点也是有后代的

所以统计链上的答案时先讨论出分界点然后用子树大小作差一下

过程繁琐。。写了才知道。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
  
const int maxn = 3E5 + 30;
const int T = 19;
  
struct E{
    int to,w; E(){}
    E(int to,int w): to(to),w(w){}
};
  
int n,tp,dfs_clock,N,A[maxn],B[maxn],dfn[maxn],stk[maxn],siz[maxn],L[maxn],
    fr[maxn],sc[maxn],nf[maxn],ns[maxn],bel[maxn],val[maxn],Ans[maxn],fa[maxn][T];
bool key[maxn];
  
vector <int> v[maxn];
vector <E> g[maxn];
stack <int> s;
  
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
  
int LCA(int p,int q)
{
    if (L[p] < L[q]) swap(p,q);
    for (int i = T - 1; i >= 0; i--)
        if (L[p] - (1 << i) >= L[q])
            p = fa[p][i];
    if (p == q) return p;
    for (int i = T - 1; i >= 0; i--)
        if (fa[p][i] != fa[q][i])
            p = fa[p][i],q = fa[q][i];
    return fa[p][0];
}
  
int Quickfa(int x,int y)
{
    for (int now = 0; y; y >>= 1,++now)
        if (y & 1) x = fa[x][now];
    return x;
}
  
void Dfs1(int x,int from)
{
    siz[x] = 1; dfn[x] = ++dfs_clock;
    for (int i = 1; i < T; i++) fa[x][i] = fa[fa[x][i-1]][i-1];
    for (int i = 0; i < v[x].size(); i++)
    {
        int to = v[x][i];
        if (to == from) continue;
        L[to] = L[x] + 1; fa[to][0] = x;
        Dfs1(to,x); siz[x] += siz[to];
    }
}
  
void Dfs2(int x)
{
    bool flag = 0; val[x] = siz[x]; fr[x] = sc[x] = maxn;
    if (key[x]) fr[x] = 0,nf[x] = x,bel[x] = -1;
    for (int i = 0; i < g[x].size(); i++)
    {
        E e = g[x][i]; Dfs2(e.to);
        val[x] -= siz[Quickfa(e.to,L[e.to]-L[x]-1)];
        int now = fr[e.to] + e.w; flag = 1;
        if (now < fr[x])
        {
            sc[x] = fr[x]; ns[x] = nf[x];
            fr[x] = now; nf[x] = nf[e.to]; bel[x] = i;
        }
        else if (now == fr[x])
        {
            if (nf[e.to] < nf[x])
            {
                sc[x] = fr[x]; ns[x] = nf[x];
                fr[x] = now; nf[x] = nf[e.to]; bel[x] = i;
            }
            else
            {
                if (sc[x] == now) ns[x] = min(ns[x],nf[e.to]);
                else sc[x] = now,ns[x] = nf[e.to];
            }
        }
        else if (now < sc[x]) sc[x] = now,ns[x] = nf[e.to];
        else if (now == sc[x]) ns[x] = min(ns[x],nf[e.to]);
    }
}
  
void Dfs3(int x,int Num,int dis)
{
    if (key[x]) Num = x,dis = 0;
    if (dis < fr[x]) Ans[Num] += val[x];
    else if (dis > fr[x]) Ans[nf[x]] += val[x];
    else if (Num < nf[x]) Ans[Num] += val[x];
    else Ans[nf[x]] += val[x];
    for (int i = 0; i < g[x].size(); i++)
    {
        E e = g[x][i]; int tot = e.w - 1;
        int Nex,len;
        if (i == bel[x])
        {
            if (sc[x] < dis) len = sc[x],Nex = ns[x];
            else if (sc[x] > dis) len = dis,Nex = Num;
            else if (ns[x] < Num) len = sc[x],Nex = ns[x];
            else len = dis,Nex = Num;
        }
        else
        {
            if (fr[x] < dis) len = fr[x],Nex = nf[x];
            else if (fr[x] > dis) len = dis,Nex = Num;
            else if (nf[x] < Num) len = fr[x],Nex = nf[x];
            else len = dis,Nex = Num;
        }
        if (tot)
        {
            int y = Quickfa(e.to,L[e.to]-L[x]-1);
            int tmp = fr[e.to] + tot + 1 - len;
            if (tmp > 0 && !(tmp&1) && tot >= (tmp >> 1))
            {
                int k = (tmp >> 1);
                int z = Quickfa(e.to,tot-k+1);
                Ans[Nex] += siz[y] - siz[z];
                y = Quickfa(e.to,tot-k);
                Ans[nf[e.to]] += siz[y] - siz[e.to];
                if (Nex < nf[e.to]) Ans[Nex] += siz[z] - siz[y];
                else Ans[nf[e.to]] += siz[z] - siz[y];
            }
            else if (len + tot < fr[e.to] + 1) Ans[Nex] += siz[y] - siz[e.to];
            else if (fr[e.to] + tot < len + 1) Ans[nf[e.to]] += siz[y] - siz[e.to];
            else
            {
                int k = fr[e.to] + tot - len >> 1;
                int z = Quickfa(e.to,tot-k);
                Ans[Nex] += siz[y] - siz[z];
                Ans[nf[e.to]] += siz[z] - siz[e.to];
            }
        }
        Dfs3(e.to,Nex,len + e.w);
    }
}
  
bool cmp(const int &x,const int &y) {return dfn[x] < dfn[y];}
void Work()
{
    N = getint();
    for (int i = 1; i <= N; i++)
        A[i] = B[i] = getint(),key[A[i]] = 1;
    sort(A + 1,A + N + 1,cmp); stk[tp = 1] = A[1]; s.push(A[1]);
    for (int i = 2; i <= N; i++)
    {
        int k = A[i],lca = LCA(k,stk[tp]);
        if (lca == stk[tp]) stk[++tp] = k;
        else
        {
            for (;;)
            {
                if (L[stk[tp-1]] > L[lca])
                    g[stk[tp-1]].push_back(E(stk[tp],L[stk[tp]]-L[stk[tp-1]])),--tp;
                else
                {
                    g[lca].push_back(E(stk[tp],L[stk[tp]]-L[lca])); --tp;
                    if (L[stk[tp]] < L[lca]) stk[++tp] = lca,s.push(lca); break;
                }
            }
            stk[++tp] = k;
        }
        s.push(k);
    }
    int rt = stk[1];
    while (tp > 1) g[stk[tp-1]].push_back(E(stk[tp],L[stk[tp]]-L[stk[tp-1]])),--tp;
    Dfs2(rt); val[rt] += n - siz[rt]; Dfs3(rt,0,maxn);
}
  
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
      
    n = getint();
    for (int i = 1; i < n; i++)
    {
        int x = getint(),y = getint();
        v[x].push_back(y);
        v[y].push_back(x);
    }
    L[1] = 1; Dfs1(1,0);
    int q = getint();
    for (int I = 1; I <= q; I++)
    {
        Work();
        for (int i = 1; i <= N; i++) printf("%d ",Ans[B[i]]);
        puts("");
        while (!s.empty())
        {
            int k = s.top(); s.pop();
            g[k].clear(); val[k] = key[k] = Ans[k] = 0;
        }
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值