树链剖分
复杂度nlog2n
边权可以转化成点权 搞一个根 把边权给到边两端深度大的哪个点 这样处理要删一个点 要删的是lca(x,y)
第一步
处理出fa deep size son
void dfs1(int u,int fa,int depth) //当前节点、父节点、层次深度 { f[u]=fa; d[u]=depth; size[u]=1; //这个点本身size=1 for(int i=head[u];i;i=e[i].next) { int v=e[i].to; if(v==fa) continue; dfs1(v,u,depth+1); //层次深度+1 size[u]+=size[v]; //子节点的size已被处理,用它来更新父节点的size if(size[v]>size[son[u]]) son[u]=v; //选取size最大的作为重儿子 } } //进入 dfs1(root,0,1);
第二步
连接重链 处理出top dfn rk 注意先处理重链再处理轻链 保证了重链的dfn值连续
void dfs2(int u,int t) //当前节点、重链顶端 { top[u]=t; id[u]=++cnt; //标记dfs序 rk[cnt]=u; //序号cnt对应节点u if(!son[u]) return; dfs2(son[u],t); /*我们选择优先进入重儿子来保证一条重链上各个节点dfs序连续, 一个点和它的重儿子处于同一条重链,所以重儿子所在重链的顶端还是t*/ for(int i=head[u];i;i=e[i].next) { int v=e[i].to; if(v!=son[u]&&v!=f[u]) dfs2(v,v); //一个点位于轻链底端,那么它的top必然是它本身 } }
求lca步骤:
如果两个是在一个重链上的(top相同) 直接选深度小的哪个为lca
否则的话每次选两个节点中top深度深的哪个节点跳
修改步骤:
如果和lca有关的话因为dfn是连续的 就相当于在跳的过程中的每个区间都区间修改区间查询 复杂度是log2n
如果是和子树有关的话 就只是和dfs序有关了 也是区间修改区间查询
将所有未被任何一条树链经过的树边删除,那么剩下的点度数都不能超过22,否则必然非法。
那么剩下的图每个连通块都只可能是链,是经典序列问题,把所有区间按左端点排序后单调栈判断即可。
#include<cstdio> #include<algorithm> #include<cstdlib> using namespace std; const int N=100010; int n,m,i,x,y,g[N],v[N<<1],nxt[N<<1],ed; int f[N],son[N],size[N],top[N],d[N]; int e[N][2]; int tag[N]; int deg[N]; int G[N],V[N<<1],NXT[N<<1],ED; bool vis[N]; int ge[N],vv[N],nxte[N],ee; void NIE(){ puts("No"); exit(0); } inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} inline void adde(int x,int y){vv[++ee]=y;nxte[ee]=ge[x];ge[x]=ee;} void dfs(int x){ d[x]=d[f[x]]+1; size[x]=1; for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){ f[v[i]]=x; dfs(v[i]); size[x]+=size[v[i]]; if(size[v[i]]>size[son[x]])son[x]=v[i]; } } void dfs2(int x,int y){ top[x]=y; if(son[x])dfs2(son[x],y); for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]&&v[i]!=son[x])dfs2(v[i],v[i]); } inline void addedge(int x,int y){ //printf("! %d %d\n",x,y); deg[x]++;deg[y]++; V[++ED]=y;NXT[ED]=G[x];G[x]=ED; V[++ED]=x;NXT[ED]=G[y];G[y]=ED; } void dfs3(int x){ for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){ dfs3(v[i]); tag[x]+=tag[v[i]]; } if(x>1&&tag[x])addedge(x,f[x]); } inline int lca(int x,int y){ while(top[x]!=top[y]){ if(d[top[x]]<d[top[y]])swap(x,y); x=f[top[x]]; } return d[x]<d[y]?x:y; } int q[N],pos[N]; int all; struct Seg{ int l,r; }seg[N],st[N]; inline bool cmp(const Seg&a,const Seg&b){ if(a.l!=b.l)return a.l<b.l; return a.r>b.r; } inline void append(int l,int r){ if(l>r)swap(l,r); seg[++all].l=l; seg[all].r=r; } inline void check(){ if(all<=1)return; sort(seg+1,seg+all+1,cmp); int i; int t=0; for(i=1;i<=all;i++){ while(t){ if(seg[i].r<=st[t].r)break; if(seg[i].l<=st[t].r)NIE(); t--; } st[++t]=seg[i]; } } inline void solve(int S){ int cnt=0; int i,j; while(1){ vis[S]=1; q[++cnt]=S; int t=0; for(i=G[S];i;i=NXT[i])if(!vis[V[i]]){ t=V[i]; } if(!t)break; S=t; } //for(i=1;i<=cnt;i++)printf("%d ",q[i]);puts(""); for(i=1;i<=cnt;i++)pos[q[i]]=i; all=0; for(i=1;i<=cnt;i++){ for(j=ge[q[i]];j;j=nxte[j]){ append(pos[q[i]],pos[vv[j]]); } } check(); } int main(){ scanf("%d%d",&n,&m); for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x); dfs(1); dfs2(1,1); for(i=1;i<=m;i++){ scanf("%d%d",&x,&y); e[i][0]=x,e[i][1]=y; int z=lca(x,y); tag[x]++; tag[y]++; tag[z]-=2; adde(x,y); } dfs3(1); for(i=1;i<=n;i++)if(deg[i]>2)NIE(); for(i=1;i<=n;i++)if(deg[i]==1&&!vis[i])solve(i); puts("Yes"); }