LCA树上差分(点覆盖&边覆盖)
问题描述
传说中的暗之连锁被人们称为Dark。Dark是人类内心的黑暗的产物,古今中外的勇者们都试图打倒它。经过研究,你发现Dark呈现无向图的结构,图中有N个节点和两类边。一类边被称为主要边,而另一类边被称为附加边。Dark有N-1条主要边,并且Dark的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark还有M条附加边。 你的任务是把Dark斩为不连通的两部分。一开始Dark的附加边都处于无敌的状态,你只能选择一条主要边切断。一旦你切断了一条主要边,Dark就会进入防御模式,主要边会变为无敌的而附加边可以被切断。但是你的能力只能再切断Dark的一条附件边。现在你想要知道,一共有多少种方案可以击败Dark。注意,就算你第一步切断主要边之后就已经把Dark斩为两截,你也需要切断一条附加边才算击败了Dark。
输入格式
第一行包含两个整数N和M。 之后N-1行,每行包含两个整数A和B,表示A和B之间有一条主要边。 之后M行以同样的格式给出附加边。
输出格式
输出一个整数表示答案
样例输入
4 1
1 2
2 3
1 4
3 4
样例输出
3
限制与约定
对于20%的数据,N<=100,M<=100。
对于100%的数据,N<=100 000,M<=200 000,数据答案保证不超过2^31-1。
wa:
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<cstdlib> using namespace std; const int maxn=1e5+5; const int maxm=2e5+5; inline int read(){ int a=0;bool b=1;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=0; x=getchar(); } while('0'<=x && x<='9'){ a=(a<<3)+(a<<1)+x-'0'; x=getchar(); } return b ? a : -a ; } int first[maxn],next[maxm*2],to[maxm*2],edge_count=1; inline void add(int x,int y){ edge_count++; to[edge_count]=y; next[edge_count]=first[x]; first[x]=edge_count; } int deep[maxn],maxdep,f[maxn][32],log[maxn]; void build(int u,int fa){ for(int i=first[u];i;i=next[i]){ int v=to[i]; if(v==fa)continue; deep[v]=deep[u]+1; f[v][0]=u; maxdep=max(maxdep,deep[v]); build(v,u); } } int n,m,ans,mark[maxn],size[maxn]; inline void LCA_init(){ for(int i=1;i<=log[maxdep];i++){ for(int j=1;j<=n;j++){ f[j][i]=f[ f[j][(i>>1)] ][ (i>>1) ]; } } } inline int LCA(int x,int y){ if(deep[y]>deep[x])swap(x,y); for(int i=log[ deep[x] ];i>=0;i--){ if(deep[y]>=deep[ f[x][i] ])x=f[x][i]; } if(x==y)return x; for(int i=log[ deep[x] ];i>=0;i--){ if(f[x][i]!=f[y][i]){ x=f[x][i];y=f[y][i]; } } return f[x][0]; } void dfs_two(int u,int fa){ size[u]=(mark[fa]+size[fa]); printf("size[%d]%d\n",u,size[u]); if(fa){ if(size[u]==1)ans++; else if(size[u]<1)ans+=m; } for(int i=first[u];i;i=next[i]){ int v=to[i]; if(v==fa)continue; dfs_two(v,u); } } int main() { n=read();m=read(); log[1]=0; for(int i=2;i<=n;i++)log[i]=log[ (i>>1) ]+1; for(int i=1,a,b;i<n;i++){ a=read();b=read(); add(a,b);add(b,a); } build(1,0); deep[1]=1; LCA_init(); for(int i=1,a,b;i<=m;i++){ a=read();b=read(); int lca=LCA(a,b);
//错误原因分析:当前节点被覆盖次数转移的时候,应从儿子转移,否则就会无辜被父亲的其他儿子转移
//边覆盖时:lca到父亲的边并不能被覆盖,所以ans[lca]-=2; if(lca==a){ mark[f[lca][0]]++;mark[b]--; } else if(lca==b){ mark[f[lca][0]]++;mark[a]--; } else { mark[f[lca][0]]++;mark[b]--;mark[a]--; } } dfs_two(1,0); printf("%d",ans); return 0; }
AC:
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<cstdlib> using namespace std; const int maxn=1e5+5; const int maxm=2e5+5; inline int read(){ int a=0;bool b=1;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=0; x=getchar(); } while('0'<=x && x<='9'){ a=(a<<3)+(a<<1)+x-'0'; x=getchar(); } return b ? a : -a ; } int first[maxn],next[maxm*2],to[maxm*2],edge_count=1; inline void add(int x,int y){ edge_count++; to[edge_count]=y; next[edge_count]=first[x]; first[x]=edge_count; } int deep[maxn],maxdep,f[maxn][32],log[maxn]; void build(int u,int fa){ for(int i=first[u];i;i=next[i]){ int v=to[i]; if(v==fa)continue; deep[v]=deep[u]+1; f[v][0]=u; maxdep=max(maxdep,deep[v]); build(v,u); } } int n,m,ans,size[maxn]; inline void LCA_init(){ for(int i=1;i<=log[maxdep];i++){ for(int j=1;j<=n;j++){ f[j][i]=f[ f[j][(i>>1)] ][ (i>>1) ]; } } } inline int LCA(int x,int y){ if(deep[y]>deep[x])swap(x,y); for(int i=log[ deep[x] ];i>=0;i--){ if(deep[y]<=deep[ f[x][i] ])x=f[x][i]; } if(x==y)return x; //printf("%d %d \n",x,y); for(int i=log[ deep[x] ];i>=0;i--){ if(f[x][i]!=f[y][i]){ x=f[x][i];y=f[y][i]; } } return f[x][0]; } void dfs_two(int u,int fa){ for(int i=first[u];i;i=next[i]){ int v=to[i]; if(v==fa)continue; dfs_two(v,u); size[u]+=size[v]; //if(u==6)printf("%d " ,size[6]); } if(size[u]==1 && fa)ans++; else if(!size[u] && fa)ans+=m; } int main() { n=read();m=read(); log[1]=0; for(int i=2;i<=n;i++)log[i]=log[ (i>>1) ]+1; for(int i=1,a,b;i<n;i++){ a=read();b=read(); add(a,b);add(b,a); } deep[1]=1;// build(1,0); LCA_init(); for(int i=1,a,b;i<=m;i++){ a=read();b=read(); int lca=LCA(a,b); size[a]++;size[b]++;size[lca]-=2;//保证树上差分是以该节点为根的子树的后缀和(simple) } dfs_two(1,0); printf("%d",ans); return 0; }
wa:
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn=3e5+5; const int maxm=3e5+5; inline int read(){ int a=0;bool b=1;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=0; x=getchar(); } while('0'<=x && x<='9'){ a=(a<<3)+(a<<1)+x-'0'; x=getchar(); } return b ? a : -a ; } int first[maxn],next[maxm*2],to[maxm*2],edge_count=1; inline void add(int x,int y){ edge_count++; to[edge_count]=y; next[edge_count]=first[x]; first[x]=edge_count; } int deep[maxn],maxdep,f[maxn][32],log[maxn]; void build(int u,int fa){ for(int i=first[u];i;i=next[i]){ int v=to[i]; if(v==fa)continue; deep[v]=deep[u]+1; f[v][0]=u; maxdep=max(maxdep,deep[v]); build(v,u); } } int n,a[maxn],ans[maxn]; inline void LCA_init(){ for(int i=1;i<=log[maxdep];i++){ for(int j=1;j<=n;j++){ f[j][i]=f[ f[j][i-1] ][i-1]; } } } inline int LCA(int x,int y){ if(deep[y]>deep[x])swap(x,y); for(int i=log[ deep[x] ];i>=0;i--){ if(deep[y]<=deep[ f[x][i] ])x=f[x][i]; } if(x==y)return x; for(int i=log[ deep[x] ];i>=0;i--){ if(f[x][i]!=f[y][i]){ x=f[x][i];y=f[y][i]; } } return f[x][0]; } inline void dfs(int u ,int fa){ for(int i=first[u];i;i=next[i]){ int v=to[i]; if(v==fa)continue; dfs(v,u); ans[u]+=ans[v]; } } int main(){ n=read(); log[1]=0; for(int i=2;i<=n;i++)log[i]=log[ (i>>1) ]+1; for(int i=1;i<=n;i++){ a[i]=read(); } for(int i=1,x,y;i<n;i++){ x=read();y=read(); add(x,y);add(y,x); } deep[1]=1; build(1,0); LCA_init(); for(int i=2;i<=n;i++){ int lca=LCA(a[i],a[i-1]);
//wrong_reason:点覆盖时,lca会被覆盖,所以不能简单的在lca_father上操作
//应该同时操作lca及其father
//或者说x到lca(x,y)权值+1,y到lca(x,y)( 不包括lca(x,y) )+1
ans[f[lca][0]]-=2;ans[a[i]]++;ans[a[i-1]]++; } dfs(1,0); for(int i=1;i<=n;i++){ printf("%d\n",ans[i]-(i!=a[1])); } return 0; }
ac:
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn=3e5+5; const int maxm=3e5+5; inline int read(){ int a=0;bool b=1;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=0; x=getchar(); } while('0'<=x && x<='9'){ a=(a<<3)+(a<<1)+x-'0'; x=getchar(); } return b ? a : -a ; } int first[maxn],next[maxm*2],to[maxm*2],edge_count=1; inline void add(int x,int y){ edge_count++; to[edge_count]=y; next[edge_count]=first[x]; first[x]=edge_count; } int deep[maxn],maxdep,f[maxn][32],log[maxn]; void build(int u,int fa){ for(int i=first[u];i;i=next[i]){ int v=to[i]; if(v==fa)continue; deep[v]=deep[u]+1; f[v][0]=u; maxdep=max(maxdep,deep[v]); build(v,u); } } int n,a[maxn],ans[maxn]; inline void LCA_init(){ for(int i=1;i<=log[maxdep];i++){ for(int j=1;j<=n;j++){ f[j][i]=f[ f[j][i-1] ][i-1]; } } } inline int LCA(int x,int y){ if(deep[y]>deep[x])swap(x,y); for(int i=log[ deep[x] ];i>=0;i--){ if(deep[y]<=deep[ f[x][i] ])x=f[x][i]; } if(x==y)return x; for(int i=log[ deep[x] ];i>=0;i--){ if(f[x][i]!=f[y][i]){ x=f[x][i];y=f[y][i]; } } return f[x][0]; } inline void dfs(int u ,int fa){ for(int i=first[u];i;i=next[i]){ int v=to[i]; if(v==fa)continue; dfs(v,u); ans[u]+=ans[v]; } } int main(){ n=read(); log[1]=0; for(int i=2;i<=n;i++)log[i]=log[ (i>>1) ]+1; for(int i=1;i<=n;i++){ a[i]=read(); } for(int i=1,x,y;i<n;i++){ x=read();y=read(); add(x,y);add(y,x); } deep[1]=1; build(1,0); LCA_init(); for(int i=2;i<=n;i++){ int lca=LCA(a[i],a[i-1]); ans[f[lca][0]]--; ans[lca]--; ans[a[i]]++; ans[a[i-1]]++; } dfs(1,0); for(int i=1;i<=n;i++){ printf("%d\n",ans[i]-(i!=a[1])); } return 0; }