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;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    素数路径Prime Path POJ3126 素数,BFS
    Fliptile POJ3279 DFS
    Find the Multiple POJ1426
    洗牌Shuffle'm Up POJ3087 模拟
    棋盘问题 POJ1321 DFS
    抓住那只牛!Catch That Cow POJ3278 BFS
    Dungeon Master POJ2251 三维BFS
    Splitting into digits CodeForce#1104A
    Ubuntu下手动安装Nvidia显卡驱动
    最大连续子序列和
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910068.html
Copyright © 2011-2022 走看看