【agc008F】Black Radius
Portal --> agc008F
Solution
?這題好神仙啊qwq瘋狂orz看懂日文題解的sjk太強啦qwq
? ?
? ?首先我們要統計的東西,是一個塗黑的連通塊,然後我們考慮找一個東西使得它可以和每個不同的連通塊(也就是不同的染色方案)一一對應,那麽這裏我們找這個連通塊的直徑的中心部分(以下簡稱中心),這個中心根據直徑的長度為奇數或者偶數可以是一個點或者一條邊
?那麽現在,如果說我們知道了中心(一個點或者一條邊),知道了半徑(就是直徑的一半),這個連通塊就確定下來了
? ?那麽現在我們要做的就是枚舉中心,然後看以其為中心能有多少種不同的半徑選擇,一種選擇方案合法當且僅當存在一個點\(x\)
?
? ?那麽接下來我們就可以愉快分類討論了
? ?為了方便下面的描述,我們先統一一些記號:
\(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)\(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