【2018/11/05測試T3】相交
阿新 • • 發佈:2018-12-19
【題目】
【分析】
一道不錯的題
首先,要知道一個東西,即若兩條路徑相交,則一條路徑的 必然在另一條路徑上
我們每加入一條邊,都計算一下之前的邊加上它對答案的貢獻
現在假設加 條邊,具體有一下幾種情況:
統計 這條路徑上的 個數:
大致長這樣:
我們給每個點一個點權,代表從這個點到根路徑上的 數量(記為 )
那麼最後的答案顯然是
每次做完更新的時候,要在 的子樹內的 ,代表它們到根的 數量多了一個(就是 )
怎麼維護這個 呢?可以用一個區間修改,單點查詢的樹狀陣列來維護(畢竟程式碼量小,常數也小)
統計穿過 的 的路徑數量:
大概長這樣:
利用類似於樹上差分的思想,在兩個端點加一,在 處減二
這樣做之後,把一個子樹內的值統計出來,就是穿過 的路徑的數量
因為,如果一條路徑在 內部,或者在外面(就是不相交的情況),那麼它的值就會被抵消掉()
而只有這種一個點在內部, 和另一個點在外部的情況(也就是合法情況)才會被統計到
那麼這個又怎麼維護呢?用一個單點修改,區間查詢的樹狀陣列就行了
一些要注意的細節:
- 兩個樹狀陣列維護的是不一樣的東西,不要弄混了
- 注意 重複的情況要單獨計算,不然會重複(以上兩種方法都會算一次)
【程式碼】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
#define M 2000005
#define lowbit(x) x&-x
using namespace std;
int n,m,t,tot;
int first[N],v[M],nxt[M];
int num[N],bit1[N],bit2[N];
int dep[N],size[N],pos[N],fa[N][25];
void add(int x,int y)
{
t++;
nxt[t]=first[x];
first[x]=t;
v[t]=y;
}
void dfs(int x)
{
int i,j;
size[x]=1,pos[x]=++tot;
for(i=1;i<=20;++i)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(i=first[x];i;i=nxt[i])
{
j=v[i];
if(j!=fa[x][0])
{
fa[j][0]=x;
dep[j]=dep[x]+1;
dfs(j);
size[x]+=size[j];
}
}
}
int Lca(int x,int y)
{
int i;
if(dep[x]<dep[y]) swap(x,y);
for(i=20;~i;--i)
if(dep[fa[x][i]]>=dep[y])
x=fa[x][i];
if(x==y) return x;
for(i=20;~i;--i)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void modify(int *bit,int i,int x)
{
while(i<=n)
{
bit[i]+=x;
i+=lowbit(i);
}
}
int query(int *bit,int i)
{
int ans=0;
while(i)
{
ans+=bit[i];
i-=lowbit(i);
}
return ans;
}
int main()
{
// freopen("access.in","r",stdin);
// freopen("access.out","w",stdout);
int x,y,i;
scanf("%d%d",&n,&m);
for(i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dep[1]=1,dfs(1);
long long ans=0;
for(i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
int lca=Lca(x,y);
ans+=num[lca],num[lca]++;
ans+=query(bit1,pos[x])+query(bit1,pos[y])-2*query(bit1,pos[lca]);
ans+=query(bit2,pos[lca]+size[lca]-1)-query(bit2,pos[lca]-1);
modify(bit1,pos[lca],1);
modify(bit1,pos[lca]+size[lca],-1);
modify(bit2,pos[x],1);
modify(bit2,pos[y],1);
modify(bit2,pos[lca],-2);
}
printf("%lld",ans);
// fclose(stdin);
// fclose(stdout);
return 0;
}