zoukankan      html  css  js  c++  java
  • 【bzoj4940】[Ynoi2016]这是我自己的发明 DFS序+树上倍增+莫队算法

    题目描述

    给一个树,n 个点,有点权,初始根是 1。
    m 个操作,每次操作:
    1. 将树根换为 x。
    2. 给出两个点 x,y,从 x 的子树中选每一个点,y 的子树中选每一个点,如果两个点点权相等,ans++,求 ans。

    输入

    第一行两个数表示 n,m。
    第二行 n 个数,表示每个点的点权 a[i]。
    之后 n - 1 行 , 每行两个数 x , y , 表示一条边
    之后 m 行,每行为 1 x 或者 2 x y。
    1 x,表示将根变成 x点。
    2 x y,表示查询 x 点的子树与 y 点的子树。
    n <= 100000 , m <= 500000 , 1 <= a[i] <= 1000000000

    输出

    对于每个询问,输出一个数表示答案。

    样例输入

    5 5
    1 2 3 4 5
    1 2
    1 3
    3 4
    3 5
    2 4 5
    2 1 5
    2 3 5
    1 5
    2 4 5

    样例输出

    0
    1
    1
    1


    题解

    DFS序+树上倍增+莫队算法

    如果没有换根操作的话,那么子树一定是dfs序上的一段区间。于是可以直接当成区间来做。

    当有换根操作时,参考 bzoj3083遥远的国度 ,可以将子树变为dfs序上至多2段区间。当根在原子树内时可以使用树上倍增法找到对应位置。

    然后的问题就转化为了 bzoj5016一个简单的询问 ,将一个询问拆成不超过9个只包含两个参数的询问,然后上莫队算法即可。

    关于莫队算法的时间复杂度:实际上为每$frac n{sqrt m}$分一块,这样左端点块内移动次数为$O(m·frac n{sqrt m}=nsqrt m)$,右端点向右移动次数为$O(sqrt m·n=nsqrt m)$,所以总时间复杂度为$O(nsqrt m)$,可以通过此题。

    然而出题人卡常,需要将块的大小*2并加上fread、fwrite才能勉强通过。。。

    #include <cmath>
    #include <cctype>
    #include <cstdio>
    #include <algorithm>
    #define N 100010
    using namespace std;
    int a[N] , v[N] , head[N] , to[N << 1] , next[N << 1] , cnt , root = 1 , fa[N][20] , deep[N] , Log[N] , w[N] , pos[N] , last[N] , tp , si , tot , cq;
    int vx[5] , ox[5] , tx , vy[5] , oy[5] , ty , cx[N] , cy[N];
    long long ans[N * 5];
    struct data
    {
    	int l , r , bl , id , opt;
    	data() {}
    	data(int L , int R , int Id , int Opt) {l = min(L , R) , r = max(L , R) , id = Id , opt = Opt;}
    	bool operator<(const data &a)const {return bl == a.bl ? r < a.r : l < a.l;}
    }q[N * 45];
    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 , w[tp] = a[x];
    	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[deep[y] - deep[x]] ; ~i ; i -- )
    		if(deep[y] - deep[x] > (1 << i))
    			y = fa[y][i];
    	return y;
    }
    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;
    }
    char pbuf[100000] , *pp = pbuf;
    inline void push(const char ch)
    {
    	if(pp == pbuf + 100000) fwrite(pbuf , 1 , 100000 , stdout) , pp = pbuf;
    	*pp ++ = ch;
    }
    inline void write(long long x)
    {
    	static char sta[20];
    	int top = 0;
    	if(!x) push('0');
    	while(x) sta[++top] = x % 10 ^ '0' , x /= 10;
    	while(top) push(sta[top -- ]);
    	push('
    ');
    }
    inline void flush()
    {
    	fwrite(pbuf , 1 , pp - pbuf , stdout);
    }
    int main()
    {
    	int n = read() , m = read() , i , j , k , t , x , y , z , px = 0 , py = 0;
    	long long now = 0;
    	for(i = 1 ; i <= n ; i ++ ) v[i] = a[i] = read();
    	sort(v + 1 , v + n + 1);
    	for(i = 1 ; i <= n ; i ++ ) a[i] = lower_bound(v + 1 , v + n + 1 , a[i]) - v;
    	for(i = 2 ; i <= n ; i ++ ) x = read() , y = read() , add(x , y) , add(y , x) , Log[i] = Log[i >> 1] + 1;
    	dfs(1);
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		t = read() , x = read();
    		if(t == 1) root = x;
    		else
    		{
    			y = read() , cq ++ ;
    			tx = ty = 0;
    			if(x == root) vx[++tx] = n , ox[tx] = 1;
    			else if(pos[root] < pos[x] || pos[root] > last[x]) vx[++tx] = last[x] , ox[tx] = 1 , vx[++tx] = pos[x] - 1 , ox[tx] = -1;
    			else z = find(x , root) , vx[++tx] = n , ox[tx] = 1 , vx[++tx] = last[z] , ox[tx] = -1 , vx[++tx] = pos[z] - 1 , ox[tx] = 1;
    			if(y == root) vy[++ty] = n , oy[ty] = 1;
    			else if(pos[root] < pos[y] || pos[root] > last[y]) vy[++ty] = last[y] , oy[ty] = 1 , vy[++ty] = pos[y] - 1 , oy[ty] = -1;
    			else z = find(y , root) , vy[++ty] = n , oy[ty] = 1 , vy[++ty] = last[z] , oy[ty] = -1 , vy[++ty] = pos[z] - 1 , oy[ty] = 1;
    			for(j = 1 ; j <= tx ; j ++ )
    				for(k = 1 ; k <= ty ; k ++ )
    					if(vx[j] && vy[k])
    						q[++tot] = data(vx[j] , vy[k] , cq , ox[j] * oy[k]);
    		}
    	}
    	si = (int)(n / sqrt(tot)) * 2 + 1;
    	for(i = 1 ; i <= tot ; i ++ ) q[i].bl = (q[i].l - 1) / si;
    	sort(q + 1 , q + tot + 1);
    	for(i = 1 ; i <= tot ; i ++ )
    	{
    		while(px < q[i].l) px ++ , now += cy[w[px]] , cx[w[px]] ++ ;
    		while(py < q[i].r) py ++ , now += cx[w[py]] , cy[w[py]] ++ ;
    		while(px > q[i].l) cx[w[px]] -- , now -= cy[w[px]] , px -- ;
    		while(py > q[i].r) cy[w[py]] -- , now -= cx[w[py]] , py -- ;
    		ans[q[i].id] += now * q[i].opt;
    	}
    	for(i = 1 ; i <= cq ; i ++ ) write(ans[i]);
    	flush();
    	return 0;
    }
    

     

  • 相关阅读:
    结对编程项目作业4
    团队编程项目进度
    团队编程项目作业2-团队编程项目代码设计规范
    现代软件工程 阅读笔记
    个人编程作业1-GIT应用
    结对编程项目作业2-开发环境搭建过程
    结对编程项目作业2-结对编项目设计文档
    课后作业-阅读任务-阅读提问
    《团队-科学计算器-模块测试过程》
    团队-科学计算器-模块开发过程
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7598214.html
Copyright © 2011-2022 走看看