1. 程式人生 > >BZOJ 3872 ant colony

BZOJ 3872 ant colony

spa truct col 推廣 zoj 直接 比較 get continue


一道比較有意思的好題目吧。

這道題其實思路應該是很有意思的。

我們註意到,這棵樹被一條關鍵的邊分成了兩部分。
當從兩邊來的數量恰好是要求的數量時,才會計算答案。
那麽我們考慮到題目中,傳遞信息的方式是固定的,
也就是說,我們只要確定了葉節點就能夠算出答案。
那麽n方暴力就很顯然了:枚舉每一個葉節點,遍歷樹計算答案。

考慮優化。
其實上述算法對與 傳遞信息方式固定 這一性質沒有很好的利用上。
這個東西它可以意味著什麽呢。
我們考慮一下如果經過關鍵邊時為x計算答案,
那麽所有與兩個端點相連的邊
也只有在當經過它時,數量在一定區間範圍內才能貢獻答案,這樣就直接逆向利用信息傳遞就可求。
同理,推廣到所有邊。
這樣最後每一個葉節點都會有一個區間,僅僅只有在這個區間內才能貢獻答案。
那麽二分查找一下就好了。時間復雜度O(nlogn)。

#include <bits/stdc++.h>
using namespace std;
inline int gi () {
    int x=0, w=0; char ch=0;
    while (! (ch>=0 && ch<=9) ) {
        if (ch==-) w=1;
        ch=getchar ();
    }
    while (ch>=0 && ch<=9) {
        x= (x<<3) + (x<<1) + (ch^48);
        ch
=getchar (); } return w?-x:x; } const int G=1e6+10; int n,g,tot,KeyEdx,KeyEdy,head[G]; long long k,Ran[G][2],Ant[G],Ind[G],Ans; struct Tree { int next, now; }t[G<<1]; inline void make (int from, int to) { t[++tot].next=head[from]; head[from]=tot; t[tot].now=to; } void
DFS (int x, int fax) { for (int i=head[x];i;i=t[i].next) { int Nex=t[i].now; if (Nex==fax) continue; if (Ind[Nex]==1) { Ran[Nex][0]=Ran[x][0], Ran[Nex][1]=Ran[x][1]; } else { Ran[Nex][0]=Ran[x][0]* (Ind[Nex]-1); Ran[Nex][1]=Ran[x][1]* (Ind[Nex]-1)+Ind[Nex]-2; } DFS (Nex, x); } } int main () { n=gi (), g=gi (), k=gi (); for (int i=1;i<=g;++i) Ant[i]=gi (); sort (Ant+1, Ant+g+1); for (int i=1, x, y;i<n;++i) { x=gi (), y=gi (); Ind[x]++, Ind[y]++; make (x, y), make (y, x); if (i==1) { KeyEdx=x; KeyEdy=y; } } if (Ind[KeyEdx]==1) Ran[KeyEdx][0]=Ran[KeyEdx][1]=k; else { Ran[KeyEdx][0]=k* (Ind[KeyEdx]-1); Ran[KeyEdx][1]=Ran[KeyEdx][0]+Ind[KeyEdx]-2; } if (Ind[KeyEdy]==1) Ran[KeyEdy][0]=Ran[KeyEdy][1]=k; else { Ran[KeyEdy][0]=k* (Ind[KeyEdy]-1); Ran[KeyEdy][1]=Ran[KeyEdy][0]+Ind[KeyEdy]-2; } DFS (KeyEdx, KeyEdy); DFS (KeyEdy, KeyEdx); for (int i=1;i<=n;++i) { if (Ind[i]>1) continue; Ans+=upper_bound (Ant+1, Ant+g+1, Ran[i][1])-lower_bound (Ant+1, Ant+g+1, Ran[i][0]); } printf ("%lld\n", Ans*k); return 0; }

BZOJ 3872 ant colony