【COGS 2434】 暗之链锁 树上差分+LCA
差分就是把一个值拆成许多差的和如 1 2 4 6 9 那么 把这个东西拆成 1 1 2 2 3 就是了,当然也可以理解为对一个问题分解为多个子问题并对其进行操作来得到原问题的答案。
树上差分就更玄妙了,它既可以把原问题拆成他到根节点的所有点,也可以拆成子树,拆成子树的话修改一个点影响的是他到根的路径上所有点,根据这个我们可以再加上LCA来解决许多问题。
这道题:I. 我们可以看出我们可以把它转化成一棵有根树,那么两部分一定是一个子树和其他 II. 那些虚边,都是砍断实边之后的藕断丝连,至于如何计算在这个实边上附上的虚边我们只需把那些虚路径乎到实路径上,就是路径修改.
#include<cstdio> #include<cstring> #include<iostream> #include<cstdlib> #define MAXN 100010 using namespace std; inline int read() { int sum=0; char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9') { sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar(); } return sum; } int w[MAXN],n,m; int f[MAXN]; struct VIA { int to,next; }c[MAXN<<1],q[MAXN<<2]; int head[MAXN],t,Head[MAXN],T; bool v[MAXN]; inline void add(int x,int y) { c[++t].to=y; c[t].next=head[x]; head[x]=t; } inline void Add(int x,int y) { q[++T].to=y; q[T].next=Head[x]; Head[x]=T; } inline void Init() { n=read(),m=read(); for(int i=1,x,y;i<n;++i)x=read(),y=read(),add(x,y),add(y,x); for(int i=1,x,y;i<=m;++i) { x=read(),y=read(); if(x==y)continue; Add(x,y),++w[x],++w[y],Add(y,x); } } inline int find(int x) { return x==f[x]?x:(f[x]=find(f[x])); } void LCA(int x,int p) { f[x]=x; for(int i=head[x];i;i=c[i].next) if(c[i].to!=p) { LCA(c[i].to,x); f[c[i].to]=x; } v[x]=1; for(int i=Head[x];i;i=q[i].next) if(v[q[i].to]) w[find(q[i].to)]-=2; } int ans; void dfs(int x) { v[x]=0; for(int i=head[x];i;i=c[i].next) if(v[c[i].to]) { dfs(c[i].to); w[x]+=w[c[i].to]; } } inline void work() { LCA(1,0); dfs(1); for(int i=2;i<=n;++i) if(w[i]==0) ans+=m; else if(w[i]==1) ++ans; printf("%d",ans); } int main() { Init(); work(); return 0; }
苟利国家生死以, 岂因祸福避趋之。