【CodeForces】983 E. NN country 树上倍增+二维数点
2018-05-23 15:11
537 查看
【题目】E. NN country
【题意】给定n个点的树和m条链,q次询问一条链(a,b)最少被多少条给定的链覆盖。\(n,m,q \leq 2*10^5\)。
【算法】树上倍增+二维数点(树状数组)
先从半链角度考虑。将每条给定链和每个询问拆成向上的一段和向下的一段。那么假设询问的半链最低端节点为x,贪心地选择x子树内向上延伸最远的给定半链(假设最高点为y),再从y继续贪心直至超过半链最顶端节点。再只考虑半链的前提下贪心显然正确。
但是我们注意到,这样贪心的复杂度是不正确的,因为如果每次只跳一条边的话复杂度就是链长了。树上跳点容易想到倍增,令\(f_{i,j}\)表示点\(i\)贪心跳\(2^j\)条链能到达的最高点,这样就可以直接倍增到最顶端节点了。注意不存在的点设为0。初始化\(f_{x,0}\)的过程可以用dfs实现,每个点比较自身出发的链的最高点和儿子子树出发的链的最高点。
接下来考虑全链。分别从两个端点倍增到最高的在lca之下的点处(需要特判一个点为另一个点的lca的情况),假设为x和y,那么接下来的问题就是判断x和y是否属于同一条链,如果是那么答案+1,否则答案+2。这实际上在询问是否有一条链的一个端点在x子树内,另一个端点在y子树内。进一步抽象成dfs序后,就是矩阵数点问题了,可以用扫描线(离线)+树状数组解决。注意数点要求有序点对,所以将所有链强制左端点的dfs序小。
复杂度\(O(n \ \ log \ \ n)\)。
#include<cstdio> #include<cstring> #include<algorithm> #define lowbit(x) (x&-x) bool isdigit(char c){return c>='0'&&c<='9';} int read(){ int s=0,t=1;char c; while(!isdigit(c=getchar()))if(c=='-')t=-1; do{s=s*10+c-'0';}while(isdigit(c=getchar())); return s*t; } using namespace std; const int maxn=200010; int n,deep[maxn],first[maxn],tot,f[maxn][22],fac[22],ans[maxn],in[maxn],ou[maxn],cnt,ask,c[maxn],ANS[maxn]; struct edge{int v,from;}e[maxn*2]; struct cyc{ int x,y,k,id; bool operator < (const cyc &a)const{ return x<a.x||(x==a.x&&id<a.id); } }d[maxn*5]; void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;} void modify(int x,int k){for(int i=x;i<=n;i+=lowbit(i))c[i]+=k;} int query(int x){if(!x)return 0;int ans=0;for(int i=x;i>=1;i-=lowbit(i))ans+=c[i];return ans;} namespace lca{ int f[maxn][22]; void dfs(int x,int fa){ in[x]=++cnt; for(int j=1;(1<<j)<=deep[x];j++)f[x][j]=f[f[x][j-1]][j-1]; for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){ deep[e[i].v]=deep[x]+1; f[e[i].v][0]=x; dfs(e[i].v,x); } ou[x]=cnt; } int lca(int x,int y){ if(deep[x]<deep[y])swap(x,y); int d=deep[x]-deep[y]; for(int j=0;(1<<j)<=d;j++)if((1<<j)&d)x=f[x][j]; if(x==y)return x; for(int j=20;j>=0;j--)if((1<<j)<=deep[x]&&f[x][j]!=f[y][j]){ x=f[x][j],y=f[y][j]; } return f[x][0]; } } void dfs(int x,int fa){ for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){ dfs(e[i].v,x); if(!f[x][0]||(f[e[i].v][0]&&deep[f[e[i].v][0]]<deep[f[x][0]]))f[x][0]=f[e[i].v][0];//!0 } } int main(){ n=read(); for(int i=2;i<=n;i++){ int u=read(); insert(u,i);insert(i,u); } lca::dfs(1,0); int m=read(); for(int i=1;i<=m;i++){ int a=read(),b=read(),t=lca::lca(a,b); if(in[a]>in[b])swap(a,b);//swap if(!f[a][0]||deep[t]<deep[f[a][0]])f[a][0]=t; if(!f[b][0]||deep[t]<deep[f[b][0]])f[b][0]=t; d[++ask]=(cyc){in[a],in[b],1,0}; } dfs(1,0); for(int x=1;x<=n;x++)if(f[x][0]==x)f[x][0]=0;//can't set itself for(int j=1;j<=20;j++)for(int x=1;x<=n;x++)f[x][j]=f[f[x][j-1]][j-1]; int q=read(); fac[0]=1;for(int i=1;i<=20;i++)fac[i]=fac[i-1]*2; for(int i=1;i<=q;i++){ int x=read(),y=read(),t=lca::lca(x,y); if(in[x]>in[y])swap(x,y);// for(int j=20;j>=0;j--)if(f[x][j]&&deep[f[x][j]]>deep[t])x=f[x][j],ans[i]+=fac[j]; for(int j=20;j>=0;j--)if(f[y][j]&&deep[f[y][j]]>deep[t])y=f[y][j],ans[i]+=fac[j]; if((!f[x][0]&&x!=t)||(!f[y][0]&&y!=t)){ans[i]=-1;continue;} if(x==t||y==t)ans[i]++;else{ ans[i]+=2; d[++ask]=(cyc){ou[x],ou[y],1,i}; d[++ask]=(cyc){in[x]-1,ou[y],-1,i}; d[++ask]=(cyc){ou[x],in[y]-1,-1,i}; d[++ask]=(cyc){in[x]-1,in[y]-1,1,i}; } } sort(d+1,d+ask+1); for(int i=1;i<=ask;i++){ if(!d[i].id){ modify(d[i].y,d[i].k); } else{ ANS[d[i].id]+=d[i].k*query(d[i].y); } } for(int i=1;i<=q;i++)printf("%d\n",ans[i]-(ANS[i]>0)); return 0; }
相关文章推荐
- codeforces 587C(树上的倍增算法)
- Codeforces 827D (Round #423 Div. 1) D. Best Edge Weight 树上倍增
- CodeForces 932D Tree(树上倍增)
- Codeforces 932D Tree 树上倍增
- Codeforces 739B(树上路径倍增及差分)
- Codeforces 587C【树上倍增】
- [codeforces 519E]E. A and B and Lecture Rooms(树上倍增)
- Codeforces 932D - Tree 【树上倍增】
- CodeForces - 842C Ilya And The Tree(树上倍增)
- codeforces 331 D3.Escaping on Beaveractor - 线段树 - 基环树 - 倍增
- 二维并查集-CodeForces 505BMr. Kitayuta's Colorful Graph
- loj#2251. 「ZJOI2017」树状数组(二维数点,树套树维护概率)
- CodeForces 702E Analysis of Pathes in Functional Graph(倍增)
- 模板 树上求LCA 倍增和树链剖分
- 【37.48%】【hdu 2587】How far away ?(3篇文章,3种做法,LCA之树上倍增)
- 【bzoj4568】[Scoi2016]幸运数字 树上倍增+高斯消元动态维护线性基
- Codeforces 828F Best Edge Weight - 随机堆 - 树差分 - Kruskal - 倍增算法
- bzoj3676:回文串(manacher+SAM的parent树上倍增)
- Noip2013 Day1 T3 货车运输(树上倍增)
- 【bzoj2783】[JLOI2012]树 树上倍增