長樂爆零之旅 day5 蘋果樹
阿新 • • 發佈:2018-12-13
這一題是書上差分和lca,可是沒看出來,一開始直接暴力了。 題目描述: 小 B 是一個辛勤的農民,他家裡種了一棵很大的蘋果樹。 這棵蘋果樹可以看作一張 n 個點 n-1 條邊的無向連通圖,小 B 覺得這顆蘋果樹很脆弱,因為只要剪斷任意一條邊,蘋果樹就不連通了,於是他給蘋果樹新加了 m 條邊。現在這顆蘋果樹就不像是一棵樹了,成了一張 n 個點 n+m-1 條邊的無向連通圖,小 Q是小 B 的好朋友,他覺得這棵樹依然很脆弱,他告訴小 B,自己只要破壞兩條邊,一條是原樹中的邊,一條是小 B 新加的邊,就能使蘋果樹不連通。 小 B 覺得小 Q 說得很不可思議,於是他找來了擅長程式設計的你,希望你告訴他,按照小B 的破壞規則,有多少種不同的方案使得蘋果樹不連通。 注意:兩種方案不同當且僅當一條邊在第一種方案中被刪除了但在第二種方案中沒有被 刪除。 輸入格式: 第一行兩個正整數 n,m,表示蘋果樹的點數和後面新加的邊的個數。 接下來 n-1 行,每行兩個正整數 u,v,表示(u,v)之間有一條樹邊。 接下來 m 行,每行兩個正整數 u,v,表示(u,v)之間有一條小 B 新加的邊。 輸出格式: 輸出一行一個整數,表示有多少種不同的方案。 輸入樣例: 4 1 1 2 2 3 1 4 3 4 輸出樣例: 3
#include<iostream>
#include<cmath>
#include<cstdio>
#include<queue>
using namespace std;
int head[600100],Next[600100],ver[600100],much[300100],size=0,t,f[300100][20],deep[300100],n,m,vist[300100];//much陣列存對應邊的作用被多少條邊替代
//f陣列存倍增的祖先,即f[i][j]表示i這個節點第2^j個祖先;deep存深度;vist存訪問
queue <int> q;//倍增預處理用的
void add (int x,int y)//邊表常規操作
{
size++;
Next[size]=head[x];
head[x]=size;
ver[size]=y;
return ;
}
void bfs()//我們預處理倍增
{
deep[1]=1;
q.push(1);
while(q.size())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=Next[i])
{
int y=ver[i];
if(deep[y]!=0)
continue;
deep[y]=deep[x]+1;//深度確定
f[y][0]=x;//dp的初始值
for(int j=1;j<=t;j++)//開始dp操作
f[y][j]=f[f[y][j-1]][j-1];
q.push(y);
}
}
return ;
}
int lca(int x,int y)//詢問兩個點的lca
{
if(deep[x]>deep[y])//我們確保y的深度比x大
swap(x,y);
for(int i=t;i>=0;i--)//把y不斷往上提
if(deep[f[y][i]]>=deep[x])
y=f[y][i];
if(x==y)//如果x和y相等,即x是y的祖先,直接返回x
return x;
for(int i=t;i>=0;i--)//我們現在確定x,y同一深度後同時往上提
if(f[y][i]!=f[x][i])//只要兩個的祖先不同就可以提
{
y=f[y][i];
x=f[x][i];
}
return f[x][0];//我們可以確保x,y一定為lca的兒子,直接返回父親
}
void dfs(int x)//我們把差分統計一下
{
vist[x]=1;
for(int i=head[x];i;i=Next[i])
{
int y=ver[i];
if(vist[y]==1)
continue;
dfs(y);
much[x]+=much[y];//在回溯時(確保兒子已經計算完了)自加上兒子(差分的常規操作)
}
}
int main()
{
long long ans=0;
scanf("%d %d",&n,&m);
t=(int)(log(n)/log(2)+1);//先計算一下log 2 n,後面倍增dp和上提要用
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d %d",&x,&y);//建立樹
add(x,y);
add(y,x);
}
bfs();
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d %d",&x,&y);//加入新邊
much[lca(x,y)]-=2;//在lca上-2(-2的原因是因為兩個後代各自+1,所以祖先要-2)
much[x]++;//後代++
much[y]++;
}
dfs(1);//dfs統計差分結果
for (int i=2;i<=n;++i)//迴圈判斷,much[1]指根到自己父親的,這一條邊其實是不存在的不過我們為了程式碼好打,同一,所以虛擬了這個邊,記得不要加
if (much[i]==1) //如果有一條邊代替
ans++;
else if (!much[i]) //如果有零條邊代替
ans+=m;
printf("%lld",ans);//給波答案
return 0;
}
唉,還有兩天國慶就gg了,傷心,藍過。