poj3417
題目其實很簡單,如果知道樹上差分是什麼基本就是秒了。
首先題面所說的,有\(n-1\)條邊,保證兩點之間只有一條簡單路徑,顯然是樹。
剩下的邊就是讓這個樹產生環的非樹邊。題面要求刪除兩條邊,使之分為兩部分,假如沒有環狀的結構,也就是沒有那些非樹邊,就只需要隨便刪除一條邊就可以達成要求。題面要求這兩條邊必須是一條樹上的邊和非樹邊。其實就已經挺明顯的了,我們對每一個非樹邊,讓這個非樹邊所連結的兩個點對應的樹上的簡單路徑上的邊全部+1,到最後,如果某一個邊只被覆蓋了一次,那麼切斷他就一定能夠找到其它的非樹邊,使得這兩個邊被切斷能夠達成題目要求。
所以只需要對每個非樹邊,把他們對應的樹上的邊全部\(+1\),最後再統計所有隻被覆蓋過一次,或者一次都沒被覆蓋的樹邊即可。
樹上差分也是比較簡單的,就是對於一個簡單路徑,如果要給他們之間的簡單路徑\(+1\),就是給這兩個節點\(+1\),然後給他們的lca\(-2\),
統計的時候就是直接統計對於每個節點的子樹的權值和\(F[x]\),這就是這個點和它父親節點之間的樹邊上的值。
然後就沒了。
為什麼re
為什麼。
我找不到錯啊啊啊啊啊啊啊啊
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <math.h>
#include <string.h>
#define ll long long
using namespace std;
inline ll read(){
ll a=0,b=1;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';
return a*b;
}
struct edge
{
ll next,to;
}e1[400001];
ll head1[400001],tot1,tot2;
ll dep[400001][22],f[400001][22],val[400001];
ll n,m;
inline void add1(ll i,ll j)
{
e1[++tot1].next=head1[i];
e1[tot1].to=j;
head1[i]=tot1;
}
void dfs(ll x,ll fa,ll now)
{
dep[x][0]=now-1;f[x][0]=fa;
for(ll i=head1[x];i!=0;i=e1[i].next)
{
ll u=e1[i].to;
if(u==fa)continue;
dfs(u,x,now+1);
}
}
inline ll ask_lca(ll x,ll y)
{
if(dep[x][0]<dep[y][0])swap(x,y);
ll now=19;
while(now>=0)
{
if(dep[x][now]>dep[y][0])x=f[x][now];
now--;
}
now=19;
if(x==y)return x;
while(now>=0)
{
if(f[x][now]!=f[y][now])x=f[x][now],y=f[y][now];
now--;
}
return f[x][0];
}
ll F[400001];
ll ans=0;
void count(ll x,ll fa)
{
F[x]+=val[x];
for(ll i=head1[x];i!=0;i=e1[i].next)
{
ll u=e1[i].to;
if(u==fa)continue;
count(u,x);
F[x]+=F[u];
}
}
int main()
{
n=read();m=read();
for(ll i=1;i<n;i++)
{
ll x=read(),y=read();
add1(x,y);add1(y,x);
}
dfs(1,0,1);
for(ll i=0;i<=18;i++)
{
for(ll j=1;j<=n;j++)
{
f[j][i+1]=f[f[j][i]][i];
dep[j][i+1]=dep[f[j][i]][i];
}
}
for(ll i=1;i<=m;i++)
{
ll x=read(),y=read();
val[x]++;val[y]++;
val[ask_lca(x,y)]-=2;
}
count(1,0);
for(ll i=2;i<=n;i++)
{
if(F[i]>1)continue;
if(F[i]==1)
ans++;
else
ans+=m;
}
cout<<ans<<endl;
return 0;
}
毀滅吧。