1. 程式人生 > >JZOJ5460. 【NOIP2017提高A組衝刺11.7】士兵訓練

JZOJ5460. 【NOIP2017提高A組衝刺11.7】士兵訓練

題目

這裡寫圖片描述

Input

第一行兩個數n,q 表示士兵數以及閱兵次數。
接下來一行n-1 個整數,第i 個整數表示士兵i+1 的直屬教官。
接下來n 行每行兩個整數i i b ,l 描述一位士兵的屬性。
接下來q 行每行一個整數i s ,表示這次閱兵的總指揮。

Output

對於每次閱兵輸出一行一個整數,表示閱兵隊伍能展現出的最大精神力P。

Sample Input

輸入1:
5 2
1 1 2 2
2 1
1 5
4 2
2 3
3 1
1
2

輸入2:
7 3
1 1 2 2 3 3
3 0
1 3
5 2
2 0
4 1
3 1
2 2
1
2
3

Sample Output

輸出1:
3
3
樣例1 解釋:
第一次閱兵時無法進行指導
第二次閱兵時令士兵3 指導士兵4

輸出2:
4
3
5

Data Constraint

30%的資料:n,q≤30
另有10%的資料:所有Si 均為1
另有20%的資料:q≤50
另有20%的資料:士兵i 的直屬教官為i-1
100%的資料: 1<=n,q<=2*10^5,0<=bi,li<=10^9,bi>=1,1<=Si<=n

題解

觀察式子,max(a%b,b%a)=min(a,b)
這個還是比較容易發現的。
先從簡單的情況入手,
如果沒有請別的人來指導,那就是求子樹裡面的嚴格次大值。
min(最大值,次大值)=次大值,這樣一定是最大的。

如果有了指導這個操作,題目就沒有這麼簡單了。
從貪心的角度想,
需要有人來指導,那肯定是要獲得最大的戰鬥力。
但是又要注意一個問題,兩個人的戰鬥力不能相同,
因為相同的時候,一取模就變為0了,顯然不是最優的。

現在就考慮需要維護些神什麼:
1、子樹裡面的最大值,
2、子樹裡面的嚴格次大值,
3、子樹裡面的嚴格次次大值,
4、子樹裡面的次大值,
5、除了這課子樹以為,li的最大值,
6、除了這課子樹以為,li的嚴格次大值,

有了這些資訊,每次詢問就可以很快地合併這些資訊,
從而快速得出答案。

前面4個東西都很好維護,
關鍵問題還是如何維護後面兩個東西。
可以用線段樹維護,還有另外一個方法。

再維護子樹裡面li的最大值、嚴格次大值。
除了這個子樹以為,就可以從除了它父親以為,以及它父親的其他兒子轉移過來。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200003
#define P putchar
using namespace std;
void read(int &n)
{
    int t=0,p=1;char ch;
    for(ch=getchar();!('0'<=ch && ch<='9');ch=getchar())
      if(ch=='-') p=-1;
    for(;'0'<=ch && ch<='9';ch=getchar()) t=t*10+ch-'0';
    n=t*p;
}

void write(int x)
{
    if(x>9)write(x/10);
    P(x%10+48);
}

int n,q,x,mx1[N],mx2[N],mx3[N],b[N],l[N],f[N],g[N],m1[N],m2[N],w[N];
int nxt[N],last[N],ans;

void up1(int x,int c)
{
    if(c>mx1[x])
    {
        mx3[x]=mx2[x];
        mx2[x]=mx1[x];
        w[x]=mx1[x];
        mx1[x]=c;
        return;
    }
    if(c<=mx1[x] && c>w[x])w[x]=c;
    if(c!=mx1[x] && c>mx2[x])
    {
        mx3[x]=mx2[x];
        mx2[x]=c;
        return;
    }
    if(c!=mx1[x] && c!=mx2[x] && c>mx3[x])mx3[x]=c;
}

void up2(int x,int c)
{
    if(c>=m1[x])
    {
        m2[x]=m1[x];
        m1[x]=c;
        return;
    }
    if(c>m2[x])m2[x]=c;
}

void up3(int x,int c)
{
    if(c>f[x])
    {
        g[x]=f[x];
        f[x]=c;
        return;
    }
    if(c!=f[x] && c>g[x])g[x]=c;
}


int max(int x,int y){return x>y?x:y;}
int min(int x,int y){if(x==y)return -1;return x<y?x:y;}

void dfs(int x)
{
    mx1[x]=b[x];
    m1[x]=l[x];
    mx2[x]=m2[x]=mx3[x]=0;
    for(int i=last[x];i;i=nxt[i])
    {
        dfs(i);
        up1(x,mx1[i]);
        up1(x,mx2[i]);
        up1(x,mx3[i]);
        up1(x,w[i]);
        up2(x,m1[i]);
        up2(x,m2[i]);
    }
}

void dfs1(int x)
{
    for(int i=last[x];i;i=nxt[i])
    {
        //f[i]=f[x];
        up3(i,f[x]);
        up3(i,g[x]);
        up3(i,l[x]);
        for(int j=last[x];j;j=nxt[j])
            if(i!=j)up3(i,m1[j]),up3(i,m2[j]);
        dfs1(i);
    }
}

int main()
{
    freopen("soldier.in","r",stdin);
    freopen("soldier.out","w",stdout);
    read(n);read(q);
    for(int i=2;i<=n;i++)
    {
        read(x);
        nxt[i]=last[x];
        last[x]=i;
    }
    for(int i=1;i<=n;i++)
        read(b[i]),read(l[i]);

    dfs(1);
    dfs1(1);

    for(int i=1;i<=q;i++)
    {
        read(x);
        ans=0;
        ans=max(ans,min(mx1[x],w[x]+f[x]));
        ans=max(ans,min(mx1[x],w[x]+g[x]));
        ans=max(ans,min(mx1[x],mx2[x]+f[x]));
        ans=max(ans,min(mx1[x],mx2[x]+g[x]));
        ans=max(ans,min(mx1[x],mx3[x]+f[x]));
        ans=max(ans,min(mx1[x],mx3[x]+g[x]));
        ans=max(ans,mx2[x]);
        if(w[x]==0)ans=0;
        write(ans);
        P('\n');
    }

    return 0;
}