zoukankan      html  css  js  c++  java
  • 【bzoj3488】[ONTAK2010]Highways DFS序+树上倍增+树状数组

    题目描述

    一棵n个点的树,给定m条路径,q次询问包含一条路径的给定路径的个数+1

    输入

    The first line of input contains a single integer N(1<=N<=100000) - the number of cities in Byteland. Cities are numbered from 1 to n . Each of the next N -1 lines contains two integers Ai, Bi(1<=Ai,Bi<=N) meaning that cities Ai and Biare connected by a road. 
    The next line contains an integer M(1<=M<=100000) - the number of highways. Each of the next m lines contains a description of a single highway. The next line contains an integer Q (1<=Q<=500000) - the number of queries. Each of the next Q lines contains a description of a query. Both highways and queries are given in the same format as the roads. 

    输出

    Your program should output exactly Q lines. The i-th line should contain the number of routes in the i-th query. 

    样例输入

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

    样例输出

    1
    4
    2
    2


    题解

    DFS序+树状数组

    咦这不是 精神污染 那道题吗?然而我那道题写得太丑了。。。

    我们不妨换个思路:考虑一条路径被什么样的路径所包含。

    当两个点x和y没有祖先关系时,显然包含它的路径的两个端点应该分别在x和y的子树内。

    当x和y具有祖先关系时,不妨设x是y的祖先,那么设x到y路径上x的儿子为z,那么包含它的路径的两个端点应该分别在z的子树外和y的子树内。

    那么就可以使用DFS序把两个端点的取值范围转化为DFS序上的一段或两段区间,其中找儿子z的过程可以使用树上倍增实现。

    于是把每个路径x-y看作平面上的点(pos[x],pos[y])(pos[x]表示x在DFS序中的位置),那么包含一条给定路径的所有路径就转化为至多2个矩形。

    所以每次询问要求的就是矩形内的点的数目,可以使用离线+树状数组解决。把每个矩形拆成前缀相减的4个点,把所有点按x坐标排序,然后使用树状数组维护y坐标的前缀和即可。

    时间复杂度$O(nlog n)$

    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #define N 100010
    using namespace std;
    struct data
    {
    	int x , y , v , id;
    	data() {}
    	data(int a , int b , int c , int d) {x = a , y = b , v = c , id = d;}
    	bool operator<(const data &a)const {return x < a.x;}
    }a[N << 1] , q[N * 30];
    int n , head[N] , to[N << 1] , next[N << 1] , cnt , fa[N][20] , deep[N] , log[N] , pos[N] , last[N] , tp , f[N] , tot , ans[N * 5];
    inline char nc()
    {
    	static char buf[100000] , *p1 , *p2;
    	return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ;
    }
    inline int read()
    {
    	int ret = 0; char ch = nc();
    	while(!isdigit(ch)) ch = nc();
    	while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ '0') , ch = nc();
    	return ret;
    }
    inline void add(int x , int y)
    {
    	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
    }
    void dfs(int x)
    {
    	int i;
    	pos[x] = ++tp;
    	for(i = 1 ; (1 << i) <= deep[x] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
    	for(i = head[x] ; i ; i = next[i])
    		if(to[i] != fa[x][0])
    			fa[to[i]][0] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
    	last[x] = tp;
    }
    inline int find(int x , int y)
    {
    	int i;
    	for(i = log[y] ; ~i ; i -- )
    		if((1 << i) <= y)
    			x = fa[x][i] , y -= (1 << i);
    	return x;
    }
    inline void update(int x)
    {
    	int i;
    	for(i = x ; i <= n ; i += i & -i) f[i] ++ ;
    }
    inline int query(int x)
    {
    	int i , ans = 0;
    	for(i = x ; i ; i -= i & -i) ans += f[i];
    	return ans;
    }
    int main()
    {
    	int m , k , i , x , y , z , p;
    	n = read();
    	for(i = 2 ; i <= n ; i ++ ) x = read() , y = read() , add(x , y) , add(y , x) , log[i] = log[i >> 1] + 1;
    	dfs(1);
    	m = read();
    	for(i = 1 ; i <= m ; i ++ ) x = read() , y = read() , a[i] = data(pos[x] , pos[y] , 0 , 0) , a[i + m] = data(pos[y] , pos[x] , 0 , 0);
    	k = read();
    	for(i = 1 ; i <= k ; i ++ )
    	{
    		x = read() , y = read();
    		if(deep[x] < deep[y]) swap(x , y);
    		if(deep[x] > deep[y] && fa[z = find(x , deep[x] - deep[y] - 1)][0] == y)
    		{
    			q[++tot] = data(pos[x] - 1 , pos[z] - 1 , -1 , i) , q[++tot] = data(pos[x] - 1 , last[z] , 1 , i) , q[++tot] = data(pos[x] - 1 , n , -1 , i);
    			q[++tot] = data(last[x] , pos[z] - 1 , 1 , i) , q[++tot] = data(last[x] , last[z] , -1 , i) , q[++tot] = data(last[x] , n , 1 , i);
    		}
    		else
    		{
    			q[++tot] = data(pos[x] - 1 , pos[y] - 1 , 1 , i) , q[++tot] = data(pos[x] - 1 , last[y] , -1 , i);
    			q[++tot] = data(last[x] , pos[y] - 1 , -1 , i) , q[++tot] = data(last[x] , last[y] , 1 , i);
    		}
    	}
    	sort(a + 1 , a + 2 * m + 1) , sort(q + 1 , q + tot + 1);
    	for(p = i = 1 ; i <= tot ; i ++ )
    	{
    		while(p <= m * 2 && a[p].x <= q[i].x) update(a[p ++ ].y);
    		ans[q[i].id] += q[i].v * query(q[i].y);
    	}
    	for(i = 1 ; i <= k ; i ++ ) printf("%d
    " , ans[i] + 1);
    	return 0;
    }
    

     

  • 相关阅读:
    对象池使用时要注意几点
    Flash3D学习计划(一)——3D渲染的一般管线流程
    714. Best Time to Buy and Sell Stock with Transaction Fee
    712. Minimum ASCII Delete Sum for Two Strings
    647. Palindromic Substrings(马拉车算法)
    413. Arithmetic Slices
    877. Stone Game
    338. Counting Bits
    303. Range Sum Query
    198. House Robber
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7725485.html
Copyright © 2011-2022 走看看