题意
分析
对每个点维护子树所能达到的dfn最大值、最小值、次大值、次小值,然后就可以计算原树中每个点与父亲的连边对答案的贡献。
- 如果子树中没有边能脱离子树,断掉该边与任意一条新加的边都成立,答案就加m。
- 如果子树中只有1条边能脱离子树,只能断掉该边和那条能脱离子树的边,答案就加1。
- 如果子树中有大于等于2条边能脱离子树,那么不能通过断边使子树独立,答案不变。
代码
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O0")
using namespace std;
template<class T> inline T read(T&x)
{
T data=0;
int w=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch==‘-‘)
w=-1;
ch=getchar();
}
while(isdigit(ch))
data=10*data+ch-‘0‘,ch=getchar();
return x=data*w;
}
typedef long long ll;
const int INF=0x7fffffff;
const int MAXN=3e5+7;
int n,m;
struct Edge
{
int nx,to;
}E[MAXN<<2];
int head[MAXN],ecnt;
void addedge(int x,int y)
{
E[++ecnt].to=y;
E[ecnt].nx=head[x],head[x]=ecnt;
}
int fa[MAXN],siz[MAXN];
int dfn[MAXN],clk;
void dfs1(int x,int f)
{
fa[x]=f,siz[x]=1;
dfn[x]=++clk;
for(int i=head[x];i;i=E[i].nx)
{
int y=E[i].to;
if(y==f)
continue;
dfs1(y,x);
siz[x]+=siz[y];
}
}
int minv[MAXN],semi[MAXN];
int maxv[MAXN],semx[MAXN];
ll ans;
void dfs2(int x)
{
for(int i=head[x];i;i=E[i].nx)
{
int y=E[i].to;
if(y==fa[x])
continue;
dfs2(y);
semi[x]=min(semi[x],max(minv[x],minv[y]));
semi[x]=min(semi[x],semi[y]);
minv[x]=min(minv[x],minv[y]);
semx[x]=max(semx[x],min(maxv[x],maxv[y]));
semx[x]=max(semx[x],semx[y]);
maxv[x]=max(maxv[x],maxv[y]);
}
int out=(minv[x]<dfn[x])+(semi[x]<dfn[x])*10+
(maxv[x]>dfn[x]+siz[x]-1)+(semx[x]>dfn[x]+siz[x]-1)*10;
/* cerr<<"check "<<x<<endl;
cerr<<" minv="<<minv[x]<<" semi="<<semi[x]<<endl;
cerr<<" maxv="<<maxv[x]<<" semx="<<semx[x]<<endl;
cerr<<" ans="<<((out==0)*m+(out==1)*1)<<endl;*/
if(x!=1)
ans+=(out==0)*m+(out==1)*1;
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
read(n);read(m);
for(int i=1;i<n;++i)
{
static int x,y;
read(x);read(y);
addedge(x,y);
addedge(y,x);
}
dfs1(1,0);
for(int i=1;i<=n;++i)
{
// cerr<<"dfn["<<i<<"]="<<dfn[i]<<endl;
minv[i]=maxv[i]=semi[i]=semx[i]=dfn[i];
}
for(int i=1;i<=m;++i)
{
static int x,y;
read(x);read(y);
if(dfn[x]>dfn[y])
swap(x,y);
if(dfn[x]<semi[y]) // edit 1:The first and the second must be updated like this.
{
semi[y]=dfn[x];
if(semi[y]<minv[y])
swap(semi[y],minv[y]);
}
if(dfn[y]>semx[x])
{
semx[x]=dfn[y];
if(semx[x]>maxv[x])
swap(semx[x],maxv[x]);
}
}
dfs2(1);
printf("%lld",ans);
// fclose(stdin);
// fclose(stdout);
return 0;
}
Hint
更新最大最小值以及次大次小值的时候,分为两种情况。
- 子树中的合并,就用吉司机线段树那种合并方式。
- 新增边时候的修改,必须用swap形式的方式修改。
有同学写vector存图被卡了,另外我的代码最慢测试点只跑了0.2s,以后要借鉴这种常数小的写法。