 /**//*
神奇!
題意:給出一棵樹,再給出M條新邊,問刪除一條樹邊以及一條新邊,使之至少變?yōu)閮刹糠值姆桨笖?shù)

對于新邊(a,b),會在a->LCA(a,b)->b這里形成一個環(huán),所以刪除新邊(a,b)以及這個環(huán)上的沒有被其他環(huán)覆蓋的邊
即可分成兩部分。所以問題轉(zhuǎn)化為求每條邊被環(huán)覆蓋的次數(shù)
設(shè)dp[x]表示x所在的父邊被覆蓋的次數(shù)
引進(jìn)一條新邊(a,b)后,dp[a]++,dp[b]++
而這個環(huán)上的其他邊的統(tǒng)計(jì)可以用treeDP解決,即for(v)
dp[u]+=dp[v]
注意到LCA(a,b)的父邊是不在環(huán)上的,所以每次引進(jìn)新邊(a,b),dp[LCA[a,b]]-=2
最后,if(dp[i]==1)ans++ 刪除該邊及覆蓋它的那個環(huán)
if(dp[i]==0)ans+=M 表明這條樹邊是橋,刪除它及任意一條新邊都可以

不明白為什么跟別人的差了300多ms
跟3694有一點(diǎn)點(diǎn)類似,新加入的邊都是在a->LCA(a,b)->b形成環(huán)
*/
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

const int MAXN = 100005;

struct Node
  {
int v,next;
}nodes[2*MAXN];

int N,M,alloc,done;
int G[MAXN],dp[MAXN];
int F[2*MAXN],B[2*MAXN],pos[MAXN];
int rmq[MAXN*2][20];

void add(int a,int b)
  {
nodes[++alloc].v=b,nodes[alloc].next=G[a];
G[a]=alloc;
}
void dfs(int u,int p,int dep)
  {
F[++done]=u,B[done]=dep;
pos[u]=done;
 for(int son=G[u];son;son=nodes[son].next) {
int v=nodes[son].v;
if(v==p)continue;
dfs(v,u,dep+1);
F[++done]=u,B[done]=dep;
}
}
void initRMQ()
  {
for(int i=1;i<=done;i++)rmq[i][0]=i;
for(int j=1;(1<<j)<=done;j++)
for(int i=1;i+(1<<j)-1<=done;i++)
if(B[rmq[i][j-1]]<B[rmq[i+(1<<(j-1))][j-1]])rmq[i][j]=rmq[i][j-1];
else rmq[i][j]=rmq[i+(1<<(j-1))][j-1];
}
int LCA(int a,int b)
  {
a=pos[a],b=pos[b];
if(a>b)swap(a,b);
int k=log(1.0+b-a)/log(2.0);
if(B[rmq[a][k]]<B[rmq[b-(1<<k)+1][k]])return F[rmq[a][k]];
else return F[rmq[b-(1<<k)+1][k]];
}
void treeDP(int u,int p)
  {
 for(int son=G[u];son;son=nodes[son].next) {
int v=nodes[son].v;
if(v==p)continue;
treeDP(v,u);
dp[u]+=dp[v];
}
}
int main()
  {
 while(~scanf("%d%d",&N,&M)) {
memset(G+1,0,sizeof(int)*N);
memset(dp+1,0,sizeof(int)*N);
done = alloc = 0;
int a,b;
 for(int i=1;i<N;i++) {
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
dfs(1,1,0);
initRMQ();
 for(int i=1;i<=M;i++) {
scanf("%d%d",&a,&b);
dp[a]++,dp[b]++;
dp[LCA(a,b)]-=2;
}
treeDP(1,1);
int ans=0;
 for(int i=2;i<=N;i++) {
if(dp[i]==0)ans+=M;
if(dp[i]==1)ans++;
}
printf("%d\n",ans);
}
return 0;
}
 /**//*
參考ST版的
不過這個會快點(diǎn)
*/
#include<cstdio>
#include<cstring>

const int MAXN = 100005;//神奇,改為100010就wa .

struct Node
  {
int v,w,next;
}nodes[4*MAXN];

int dp[MAXN],G[MAXN],Q[MAXN];
int fa[MAXN],ans[MAXN];
int N,M,alloc;
bool vi[MAXN];

void add1(int a,int b)
  {
alloc++;
nodes[alloc].v=b,nodes[alloc].next=G[a];
G[a]=alloc;
}
void add2(int a,int b,int c)
  {
alloc++;
nodes[alloc].v=b,nodes[alloc].w=c;
nodes[alloc].next=Q[a];
Q[a]=alloc;
}
int find(int a)
  {
if(fa[a]==a)return a;
return fa[a]=find(fa[a]);
}

void Tarjan(int u)
  {
vi[u]=1;
 for(int son=Q[u];son;son=nodes[son].next) {
int v=nodes[son].v,id=nodes[son].w;
if(vi[v])ans[id]=find(v);
}
 for(int son=G[u];son;son=nodes[son].next) {
int v=nodes[son].v;
 if(!vi[v]) {
Tarjan(v);
fa[v]=u;
}
}
}
void treeDP(int u,int p)
  {
 for(int son=G[u];son;son=nodes[son].next) {
int v=nodes[son].v;
if(v==p)continue;
treeDP(v,u);
dp[u]+=dp[v];
}
}

int main()
  {
 while(~scanf("%d%d",&N,&M)) {
memset(G+1,0,sizeof(int)*N);
memset(Q+1,0,sizeof(int)*N);
memset(dp+1,0,sizeof(int)*N);
memset(vi+1,0,sizeof(int)*N);
alloc=0;
int a,b;
for(int i=1;i<=N;i++)fa[i]=i;
 for(int i=1;i<N;i++) {
scanf("%d%d",&a,&b);
add1(a,b);
add1(b,a);
}
 for(int i=1;i<=M;i++) {
scanf("%d%d",&a,&b);
add2(a,b,i);
add2(b,a,i);
dp[a]++;
dp[b]++;
}
Tarjan(1);
for(int i=1;i<=M;i++)
dp[ans[i]]-=2;
treeDP(1,1);
int ans=0;
 for(int i=2;i<=N;i++) {
if(dp[i]==0)ans+=M;
if(dp[i]==1)ans++;
}
printf("%d\n",ans);
}
return 0;
}

|