首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案。根据实现不同有下面几种方法。
三个log:假如对每个点都存下经过它的链并S[x],那么每新加一条路径进来的时候,相当于在路径上所有点的S中都加入这条路径。树剖之后,相当于对log个区间中的点都加入log个区间。具体实现有树剖后线段树维护虚树、矩形扫描线、线段树+set存区间等多种方法,这里不再多说。
两个log:先树剖,然后对每个点开一棵线段树存储它的S,由于题中没有修改,所以可以树上差分+线段树合并,这样可以将方法一中“需要修改的区间数”的log去掉了。
一个log:发现就是对每个点求所有经过它的路径的端点的斯坦纳树(这里一个点集的斯坦纳树是指原树上最小的点集,满足包含这个点集且连通)。考虑如何暴力求一个点集的斯坦纳树,那显然就是将它们按DFS序排序后,所有点深度之和减去每对相邻点LCA的深度和。为了方便我们将点集中强制加入根,最后求出结果后再减去所有点LCA的深度的两倍。以DFS序为下标建线段树,每个点维护它所代表的DFS区间中,所有在点集中的点(加上根)构成的斯坦纳树的大小。两个区间的合并就是两边的斯坦纳树大小之和,减去左边区间里在点集中的DFS序最大的点与右边区间里在点集中的DFS序最小的点的LCA的深度,于是再维护区间里在点集中的DFS序最大/小的点分别是谁即可。同样使用树上差分+线段树合并,就可以将方法一中“每个修改区间中要加入的区间数”的log去掉了。

(参考https://www.luogu.org/blog/Sooke/solution-p5327

 #include<cstdio>
#include<vector>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
typedef long long ll;
using namespace std; const int N=,M=,K=;
int n,m,x,y,tim,cnt,nd,d[N],lg[N],rt[N],fa[N],dfn[N],st[N][];
int v[M],ls[M],rs[M],s[M],t[M],c[M],h[N],to[N],nxt[N];
ll ans;
vector<int>del[N]; void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } void dfs(int x){
d[x]=d[fa[x]]+; st[++tim][]=x; dfn[x]=tim;
For(i,x) if ((k=to[i])!=fa[x]) fa[k]=x,dfs(k),st[++tim][]=x;
} void init(){
rep(j,,lg[tim]) rep(i,,tim-(<<j)+){
int x=st[i][j-],y=st[i+(<<(j-))][j-];
st[i][j]=d[x]<d[y] ? x : y;
}
} int lca(int x,int y){
if (!x || !y) return ;
x=dfn[x]; y=dfn[y];
if (x>y) swap(x,y);
int t=lg[y-x+]; x=st[x][t]; y=st[y-(<<t)+][t];
return d[x]<d[y] ? x : y;
} void upd(int x){
v[x]=v[ls[x]]+v[rs[x]]-d[lca(t[ls[x]],s[rs[x]])];
s[x]=s[ls[x]] ? s[ls[x]] : s[rs[x]];
t[x]=t[rs[x]] ? t[rs[x]] : t[ls[x]];
} void mdf(int &x,int L,int R,int p,int k){
if (!x) x=++nd;
if (L==R){ c[x]+=k; v[x]=(c[x]?d[p]:); s[x]=t[x]=(c[x]?p:); return; }
int mid=(L+R)>>;
if (dfn[p]<=mid) mdf(ls[x],L,mid,p,k); else mdf(rs[x],mid+,R,p,k);
upd(x);
} int merge(int x,int y,int L,int R){
if (!x || !y) return x|y;
if (L==R){ c[x]+=c[y]; v[x]|=v[y]; s[x]|=s[y]; t[x]|=t[y]; return x; }
int mid=(L+R)>>; ls[x]=merge(ls[x],ls[y],L,mid); rs[x]=merge(rs[x],rs[y],mid+,R);
upd(x); return x;
} void solve(int x){
For(i,x) if ((k=to[i])!=fa[x]) solve(k);
int ed=del[x].size()-;
rep(i,,ed) mdf(rt[x],,tim,del[x][i],-);
ans+=v[rt[x]]-d[lca(s[rt[x]],t[rt[x]])]; rt[fa[x]]=merge(rt[fa[x]],rt[x],,tim);
} int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d",&n,&m);
rep(i,,n<<) lg[i]=lg[i>>]+;
rep(i,,n) scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs(); init();
rep(i,,m){
scanf("%d%d",&x,&y); int l=lca(x,y);
mdf(rt[x],,tim,x,); mdf(rt[x],,tim,y,);
mdf(rt[y],,tim,x,); mdf(rt[y],,tim,y,);
del[l].push_back(x); del[l].push_back(y);
del[fa[l]].push_back(x); del[fa[l]].push_back(y);
}
solve(); printf("%lld\n",ans/);
return ;
}

最新文章

  1. 忘记XP密码的解决方案
  2. unity3d UGUI多语言
  3. Android随笔之——获取EditText光标所在行行号
  4. Java语言中的基本词汇
  5. OkHttp使用全解析(转)。
  6. (转)Yale CAS + .net Client 实现 SSO(3)
  7. web服务构架
  8. [置顶] android关机闹钟设计思路
  9. nutch solr 配置
  10. mybatis-spring最新版下载地址
  11. 左手是“Python”的身体,右手是“R”的灵魂,你爱哪个?
  12. JAVA获取运行程序的src路径
  13. tomcat/Java指定加载jar包的路径
  14. 12外观模式Facade
  15. crontab使用说明及例子程序
  16. eclipse中git更新操作
  17. 第七章&#160;二叉搜索树 (d2)AVL树:插入
  18. win7结束进程 时,提示“拒绝访问”、“没有此任务的实例运行”怎么办?
  19. c++ list 合并list
  20. python装饰器 高阶函数 函数闭包

热门文章

  1. Kubernetes addons 之 coredns部署
  2. The Practical Importance of Feature Selection(变量筛选重要性)
  3. 005 Spring和SpringBoot中的@Component 和@ComponentScan注解
  4. C# Area 双重路由
  5. 生成model笔记
  6. 转「服务器运维」如何解决服务器I/O过高的问题
  7. C/C++代码静态分析工具调研
  8. How to Plan and Configure YARN and MapReduce 2 in HDP 2.0
  9. 转Python 爬虫入门实战
  10. Linux之sudo免密码操作