1. 程式人生 > >【agc008F】Black Radius

【agc008F】Black Radius

edge 黑點 分享圖片 調整 ems ont 並且 black info

Portal --> agc008F

Solution

  ?這題好神仙啊qwq瘋狂orz看懂日文題解的sjk太強啦qwq

?  ?

?  ?首先我們要統計的東西,是一個塗黑的連通塊,然後我們考慮找一個東西使得它可以和每個不同的連通塊(也就是不同的染色方案)一一對應,那麽這裏我們找這個連通塊的直徑的中心部分(以下簡稱中心),這個中心根據直徑的長度為奇數或者偶數可以是一個點或者一條邊

  ?那麽現在,如果說我們知道了中心(一個點或者一條邊),知道了半徑(就是直徑的一半),這個連通塊就確定下來了

?  ?那麽現在我們要做的就是枚舉中心,然後看以其為中心能有多少種不同的半徑選擇,一種選擇方案合法當且僅當存在一個點\(x\)

和一個非負整數\(d\)能夠將其構造出來

  ?

?  ?那麽接下來我們就可以愉快分類討論了

?  ?為了方便下面的描述,我們先統一一些記號:

\(depmx[x]\):以\(x\)的子樹的最大深度(關於這個子樹到底是哪種,依下面的描述定)

\(dis(x,y)\)\(x\)\(y\)在樹上的簡單路徑長度

  ?

?  ?先看比較簡單的情況:中心是一條邊\((u,v)\)

技術分享圖片

?  ?我們用三角形直觀地表示其“子樹”,三角形的大小反映的是\(depmx[u]\)\(depmx[v]\)的大小關系,那麽這條邊為中心的貢獻是:如果\(u\)中(\(depmx\)較小的那個點)有可選的點,那麽對答案有\(1\)

的貢獻,否則沒有貢獻

?  ?具體是因為,我們要保證存在一個\(x\)\(d\)能夠構造這種方案,那麽假設\(u\)中有可選的點,我們如果要構造出這個連通塊並且保證\((u,v)\)是中心的話,\(u\)的整個“子樹”中的所有點必須塗完,否則無法塗到\(v\)的子樹中對應的位置,\((u,v)\)就不是中心了

?  ?而如果說\(u\)中沒有可選的點,考慮選擇\(v\)中的一個點來構造,會發現這是不行的,因為兩邊必定不對稱,無法保證\((u,v)\)是中心

?   ?

?  ?接下來是稍微復雜一點的情況:中心是一個點\(u\)

技術分享圖片

?  ?同樣的我們還是直觀地用三角形表示子樹,然後用三角形的。。高度來表示\(depmx\)

之間的相對大小,有黑點表示這個子樹中有可選的點,沒有反之,為了方便下面的表述,我們給子樹編號為\(1,2,3,4\)

?  ?感性理解一下。。半徑的可選範圍應該是一段區間,那麽現在我們要做的就是卡出上界和下界

?  ?這裏我們還需要分兩個小類討論

(1)\(u\)是一個可選點

?  ?如果\(u\)可選,那麽下界顯然就是\(0\)(只塗自己一個點),上界的話就是所有後繼子樹中(也就是上圖中的\(1,2,3,4\)號子樹)\(depmx\)次大值

?  ?為什麽不能是最大值呢?因為一旦半徑是最大值,顯然\(4\)號子樹會被塗完,\(1\sim 3\)號子樹也會被塗完,但是這個時候直徑的中點就不是我們欽定的中心\(u\)了,只有在\(depmx\)次大值之前,我們可以保持這樣一個狀態:

技術分享圖片

?  ?橙色線以上是塗色塗到的部分,過\(u\)的橙色線是直徑,只要半徑的長度是\(<=depmx\)次大值的,我們一定可以將直徑的兩個端點定在次深子樹的“邊界”和最深子樹的“邊界”上,這樣一定能保證\(u\)是中心,而如果\(>depmx\)次大值了,中心就會向最深子樹那個方向偏移

  ?所以這部分的貢獻是:\(depmx\)次大值+1

  ?

(2)\(u\)不是一個可選點

?  ?如果\(u\)不可選,那麽顯然下界是需要調整的

?  ?下界應該調整為有可選點的子樹中\(depmx\)的最小值,因為為了塗到\(u\)並且保證\(u\)為直徑的中點,最小的那個子樹必須塗完(具體可以考慮,假設一個我們選擇的\(x\)在這個最小的子樹內,那麽\(x\)自然要先塗到\(u\),這樣\(x\)以下的節點也會被塗到,然後為了保證\(u\)是中心,這個半徑的長度還需要繼續擴大,對應的\(x\)下更深的節點也會繼續被塗到,這樣一直延伸直到\(x\)所在的整個子樹都被塗完,\(x\)在其他子樹中的情況類似)

?  ?接著就是上界,我們是否可以繼續使用(1)中的結論呢?

?  ?答案是肯定的,同樣我們也可以考慮,假設\(x\)在次深子樹中,同樣延伸的話最後會將次深子樹塗完,然後我們可以得到一條類似(1)中所說的那樣的直徑,\(x\)在其他子樹中類似

?  ?所以這部分的貢獻是:有可選點的子樹的\(depmx\)的最小值-\(depmx\)次小值+1

?   ?

?  ?註意,如果說沒有後繼兒子,這個點的貢獻就是:如果這個點是可選點,貢獻為\(1\),否則貢獻為\(0\)

  ?

?  ?然後我們就可以快樂樹d在\(O(n)\)內預處理出往下的\(depmx\)和往上的\(depmx\)然後就可以快樂求解啦

  ?

?  ?代碼大概長這個樣子

#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
const int N=2*(1e5)+10,inf=2147483647;
struct xxx{
    int y,nxt,x;
}a[N*2];
struct Data{/*{{{*/
    int mx,smx,idmx;
    void init(){mx=-1; smx=-1; idmx=-1;}
    void update(int delta,int id1){
        if (mx<delta)
            smx=mx,mx=delta,idmx=id1;
        else
            smx=max(smx,delta);
    }
}info[N];/*}}}*/
char s[N];
int h[N],depmx[N][2],ok[N],cnt[N][2],dep[N];
int n,m,tot,all;
ll ans;
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot; a[tot].x=x;}
void dfs(int fa,int x,int d){
    int u;
    dep[x]=d;
    depmx[x][0]=depmx[x][1]=0;
    info[x].init();
    info[x].update(0,x);
    cnt[x][0]=cnt[x][1]=ok[x];
    for (int i=h[x];i!=-1;i=a[i].nxt){
        u=a[i].y;
        if (u==fa) continue;
        dfs(x,u,d+1);
        depmx[x][0]=max(depmx[x][0],depmx[u][0]+1);
        info[x].update(depmx[u][0]+1,u);
        cnt[x][0]+=cnt[u][0];
    }
}
void dfs1(int fa,int x){
    int u;
    if (fa){
        if (x==info[fa].idmx){
            if (info[fa].smx!=-1){
                depmx[x][1]=max(depmx[x][1],info[fa].smx+1);
                info[x].update(info[fa].smx+1,fa);
            }
        }
        else{
            if (info[fa].mx!=-1){
                depmx[x][1]=max(depmx[x][1],info[fa].mx+1);
                info[x].update(info[fa].mx+1,fa);
            }
        }
        cnt[x][1]=cnt[fa][1]+cnt[fa][0]-cnt[x][0];
    }
    for (int i=h[x];i!=-1;i=a[i].nxt){
        u=a[i].y;
        if (u==fa) continue;
        dfs1(x,u);
    }
}
int solve_edge(int i){
    int x=a[i].x,y=a[i].y,tmp;
    int depx,depy,cntx,cnty;
    if (dep[x]>dep[y]) swap(x,y);
    depy=depmx[y][0]; cnty=cnt[y][0];
    cntx=all-cnty;
    if (y==info[x].idmx) depx=info[x].smx;
    else depx=info[x].mx;
    if (depx==depy&&(cntx||cnty)) return 1;
    if (depx<depy&&cntx==0) return 0;
    if (depy<depx&&cnty==0) return 0;
    return 1;
}
int solve_vertice(int x){
    int son=0,u;
    for (int i=h[x];i!=-1;i=a[i].nxt) ++son;
        //son+=dep[a[i].y]<dep[x]?cnt[a[i].y][1]:cnt[a[i].y][0];
    if (son<=1) return ok[x];
    int up,dw=inf,smx=-1,mx=depmx[x][1];
    if (depmx[x][1]>0&&all-cnt[x][0]) dw=min(dw,depmx[x][1]);
    for (int i=h[x];i!=-1;i=a[i].nxt){
        u=a[i].y;
        if (dep[u]<dep[x]) continue;
        if (cnt[u][0])
            dw=min(dw,depmx[u][0]+1);
        if (depmx[u][0]+1>mx)
            smx=mx,mx=depmx[u][0]+1;
        else
            smx=max(smx,depmx[u][0]+1);
    }
    up=smx;
    if (ok[x]) dw=0;
    if (dw==inf) return 0;
    if (up<dw) return 0;
    return up-dw+1;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    int x,y;
    scanf("%d",&n);
    memset(h,-1,sizeof(h));
    tot=0;
    for (int i=1;i<n;++i){
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    scanf("%s",s+1);
    all=0;
    for (int i=1;i<=n;++i) ok[i]=s[i]-'0',all+=ok[i];
    dfs(0,1,1);
    dfs1(0,1);
    for (int i=1;i<=tot;i+=2) 
        ans+=solve_edge(i);
    for (int i=1;i<=n;++i) 
        ans+=solve_vertice(i);
    printf("%lld\n",ans);
}

【agc008F】Black Radius