zoukankan      html  css  js  c++  java
  • JZOJ 6276. 【noip提高组模拟1】树(DFS序+扫描线)

    JZOJ 6276. 【noip提高组模拟1】树

    题目

    Description

    有一棵n个节点的无根树,给出其中的m对点对<x,y>。问有多少条树上的简单路径<u,v>满足该路径上不存在任何一对给出的点对<x,y>。
    这里我们认为路径<u,v>和<v,u>是相同的。并且对于题目中给出的点对<x,y>满足x!=y,对于你要计数的路径<u,v>满足u!=v(即单点不算答案)。

    Input

    第一行两个正整数n,m。
    接下来n-1行每行两个正整数u,v描述树上的一条边。
    接下来m行每行两个正整数x,y描述一对给出的点对。
    (注意,这里我们不保证同样的点对<x,y>不会重复出现)

    Output

    一行一个整数,表示满足要求的树上简单路径的条数。

    Sample Input

    8 3
    1 2
    1 3
    4 8
    2 4
    2 5
    3 6
    3 7
    2 3
    4 8
    6 7

    Sample Output

    11

    Data Constraint

    在这里插入图片描述

    Hint

    满足条件的路径为 &lt; 1 , 2 &gt; , &lt; 1 , 3 &gt; , &lt; 1 , 4 &gt; , &lt; 1 , 5 &gt; , &lt; 1 , 6 &gt; , &lt; 1 , 7 &gt; , &lt; 2 , 4 &gt; , &lt; 2 , 5 &gt; , &lt; 3 , 6 &gt; , &lt; 3 , 7 &gt; , &lt; 4 , 5 &gt; &lt;1,2&gt;,&lt;1,3&gt;,&lt;1,4&gt;,&lt;1,5&gt;,&lt;1,6&gt;,&lt;1,7&gt;,&lt;2,4&gt;,&lt;2,5&gt;,&lt;3,6&gt;,&lt;3,7&gt;,&lt;4,5&gt; <1,2><1,3><1,4><1,5><1,6><1,7><2,4><2,5><3,6><3,7><4,5>

    题解

    • 观察题目,易得答案为总路径条数(不重复)减去不符合条件的路径条数,
    • 对于一个 &lt; x , y &gt; &lt;x,y&gt; <x,y>,令其 d e e p [ x ] ≥ d e e p [ y ] deep[x]≥deep[y] deep[x]deep[y]
    • 情况一:若 y y y x x x的祖先,则 x x x子树内的所有点 n n n个点中除了 y y y的子树以外的所有点之间相互的路径都是不合法的,因为路径必定经过 x x x y y y
    • 情况二:若 x x x y y y没有祖先关系,则 x x x子树内的所有点 y y y子树内的所有点之间相互的路径都是不合法的,它们必定经过 x x x y y y
    • 但对于不同的 &lt; x , y &gt; &lt;x,y&gt; <x,y>,这些路径会有重复,所以不能单纯地直接计算,那怎么办呢?
    • 先把 n n n个点的 D F S DFS DFS序求出来,把不能互相连通的两个区间分别作为矩形的横纵坐标位置,将其覆盖在坐标系中,
    • [ x 1 , x 2 ] [x1,x2] [x1,x2]这是一个区间)与 [ y 1 , y 2 ] [y1,y2] [y1,y2]不能相互到达,那么在 x 1 x1 x1处, [ y 1 , y 2 ] [y1,y2] [y1,y2]区间打上 + 1 +1 +1标记,在 x 2 + 1 x2+1 x2+1处, [ y 1 , y 2 ] [y1,y2] [y1,y2]区间打上 − 1 -1 1标记,
    • 那么对应的区间怎么表示呢?
    • 情况一:分两个区间,情况二:分一个区间。(请自行思考,用 d f n [ ] dfn[] dfn[] s i z e [ ] size[] size[]
    • 然后扫描线从左往右扫,用线段树区间修改维护,因为每个加入的区间,后面必定会对应的删除,所以这是标记不下传的区间修改
    • 记录 s [ v ] s[v] s[v]表示线段树上当前区间整段被覆盖的次数, f [ v ] f[v] f[v]为整个区间未被覆盖的总长度,
    • 每次 + 1 +1 +1时, s [ v ] + + s[v]++ s[v]++ f [ v ] = 0 f[v]=0 f[v]=0
    • 每次 − 1 -1 1时, s [ v ] − − s[v]-- s[v],当 s [ v ] = 0 s[v]=0 s[v]=0时, f [ v ] = f [ l ] + f [ r ] f[v]=f[l]+f[r] f[v]=f[l]+f[r](由左右子树更新自己的值),
    • 同时回溯时更新 f [ v ] = f [ l ] + f [ r ] f[v]=f[l]+f[r] f[v]=f[l]+f[r]
    • a n s ans ans每次累加 [ i + 1 , n ] [i+1,n] [i+1,n]的答案(这样才不会重复)。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define LL long long
    #define N 100010
    int last[N],next[N*2],to[N*2],len=0;
    int dfn[N],si[N],dp[N],f[N][20];
    int qs=0,bz[N*4];
    LL g[N*4],s=0;
    struct node
    {
    	int x,l,r,c;
    }q[N*4];
    void add(int x,int y)
    {
    	to[++len]=y;
    	next[len]=last[x];
    	last[x]=len;
    }
    void dfs(int k,int fa)
    {
    	dp[k]=dp[fa]+1,si[k]=1,dfn[k]=++dfn[0];
    	f[k][0]=fa;
    	for(int i=1;i<20;i++) f[k][i]=f[f[k][i-1]][i-1];
    	for(int i=last[k];i;i=next[i]) if(to[i]!=fa)
    	{
    		dfs(to[i],k);
    		si[k]+=si[to[i]];
    	}
    }
    void insert(int x,int xx,int y,int yy)
    {
    	if(x>xx) return;
    	if(y>yy) return;
    	if(y<x) swap(x,y),swap(xx,yy);
    	q[++qs].x=x,q[qs].l=y,q[qs].r=yy,q[qs].c=1;
    	q[++qs].x=xx+1,q[qs].l=y,q[qs].r=yy,q[qs].c=-1;
    }
    int cmd(node x,node y) 
    {
    	return x.x<y.x;
    }
    void make(int v,int l,int r,int x,int y,int c)
    {
    	if(l==x&&r==y)
    	{
    		bz[v]+=c;
    		if(bz[v]) g[v]=r-l+1;
    		if(!bz[v])
    		{
    			if(l!=r) g[v]=g[v*2]+g[v*2+1]; else g[v]=0;	
    		}
    	}
    	else
    	{
    		int mid=(l+r)/2;
    		if(y<=mid) make(v*2,l,mid,x,y,c);
    		else if(x>mid) make(v*2+1,mid+1,r,x,y,c);
    		else make(v*2,l,mid,x,mid,c),make(v*2+1,mid+1,r,mid+1,y,c);
    		if(!bz[v]) g[v]=g[v*2]+g[v*2+1];
    	}
    }
    void find(int v,int l,int r,int x,int y)
    {
    	if(l==x&&r==y)
    	{
    		s+=r-l+1-g[v];
    	}
    	else
    	{
    		int mid=(l+r)/2;
    		if(y<=mid) find(v*2,l,mid,x,y);
    		else if(x>mid) find(v*2+1,mid+1,r,x,y);
    		else find(v*2,l,mid,x,mid),find(v*2+1,mid+1,r,mid+1,y);
    	}
    }
    int main()
    {
    	int n,m,i,j,x,y;
    	scanf("%d%d",&n,&m);
    	for(i=1;i<n;i++) 
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y),add(y,x);
    	}
    	dfs(1,0);
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		if(dp[x]<dp[y]) swap(x,y);
    		int tx=x;
    		if(dp[x]==dp[y])
    		{
    			insert(dfn[x],dfn[x]+si[x]-1,dfn[y],dfn[y]+si[y]-1);
    			continue;
    		}
    		for(j=19;j>=0;j--) if(dp[x]-(1<<j)>dp[y]) x=f[x][j];
    		int tp=x;
    		x=f[x][0];
    		if(x==y)
    		{
    			x=tx;
    			insert(dfn[x],dfn[x]+si[x]-1,1,dfn[tp]-1);
    			insert(dfn[x],dfn[x]+si[x]-1,dfn[tp]+si[tp],n);
    		}
    		else
    		{
    			x=tx;
    			insert(dfn[x],dfn[x]+si[x]-1,dfn[y],dfn[y]+si[y]-1);
    		}
    	}
    	sort(q+1,q+qs+1,cmd);
    	j=1;
    	for(i=1;i<n;i++)
    	{
    		while(j<=qs&&q[j].x==i)
    		{
    			make(1,1,n,q[j].l,q[j].r,q[j].c);
    			j++;
    		}
    		find(1,1,n,i+1,n);
    	}
    	printf("%lld
    ",s);
    	return 0;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    《JS实现复制内容到剪贴板功能,可兼容所有PC浏览器,不兼容手机端》
    《Web开发中块级元素与行内元素的区分》
    《Web开发中让盒子居中的几种方法》
    《Javascript、jQuery获取各种屏幕的宽度和高度方法》
    《Web开发过滤Javascript、HTML的方法》
    《移动端浏览器Touch事件判断手指滑动方向方法》
    《C#微信开发系列(4)-接收 / 返回文本消息》
    《C#微信开发系列(3)-获取接口调用凭据》
    django rest-framework
    git
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910068.html
Copyright © 2011-2022 走看看