zoukankan      html  css  js  c++  java
  • CCPC河南省赛B-树上逆序对| 离线处理|树状数组 + 线段树维护逆序对 + dfs序 + 离散化

    B题地址:树上逆序对

    有两个思路
    方法一:线段树离线 + 树状数组或者线段树维护区间和

    0:离散化,离线存储输入的operation操作序列。

    ①:先线段树在dfs序上离线处理好整一棵树:在dfs序上先查询"加入当前结点的逆序对权值和"并记录,再加入当前这个节点;dfs完毕后,就已经记录好每个结点的dfs序出入时间戳(转化成区间问题了)和每个结点逆序对权值。

    ②:使用树状数组或者新的线段树在dfs序上插入逆序对权值

    为什么能这样呢?因为dfs序维护了每个结点遍历的顺序,每个结点的dfs序时间戳肯定比它的子孙结点们小,离线后就可以重新再更新新的树状数组或线段树,加点权(下标是dfs序、权值是每个点加入前,其它所有点它的逆序对个数)。

    方法二:主席树,利用主席树的历史版本在线建树,不受影响,待补

    写了方法一的,已AC~

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 2e5+10;
    const int inf = 0x3f3f3f3f;
    vector<int> vec;
    vector<int> g[maxn];
    int n,m;
    int a[maxn],in[maxn],out[maxn],cnt = 0,num = 0,unum = 0;
    int tree[maxn*4];
    ll res[maxn],c[maxn*2];
    
    struct node{
    	int type;
    	int u;
    	int x;
    }op[maxn];
    
    //线段树 单点更新 区间查询
    void pushup(int o){
    	tree[o] = tree[o<<1] + tree[o<<1|1];
    }
    
    void build(int o,int l,int r){
    	if(l == r){
    		tree[o] = 0;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(o<<1,l,mid);
    	build(o<<1|1,mid+1,r);
    	pushup(o);
    }
    
    void update(int o,int l,int r,int pos,ll v){
    	if(l == r){
    		tree[o] += v;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(pos <= mid) update(o<<1,l,mid,pos,v);
    	else update(o<<1|1,mid+1,r,pos,v);
    	pushup(o);
    }
    
    ll query(int o,int l,int r,int ql,int qr){
    	if(l > r) return 0;
    	if(ql <= l && r <= qr) return tree[o];
    	int mid = (l + r) >> 1;
    	ll result = 0;
    	if(ql <= mid) result += query(o<<1,l,mid,ql,qr);
    	if(qr > mid) result += query(o<<1|1,mid+1,r,ql,qr);
    	return result;
    }
    
    //树状数组 维护区间和 
    int lowbit(int x){
    	return x & -x;
    }
    
    void add(int x,int v){
    	while(x < maxn){
    		c[x] += v;
    		x += lowbit(x);
    	}
    }
    
    ll ask(int x){
    	if(x >= maxn) return 0;
    	ll res = 0;
    	while(x){
    		res += c[x];
    		x -= lowbit(x);
    	}
    	return res;
    }
    
    ll rangeAsk(int l,int r){
    	return ask(r) - ask(l - 1);
    }
    
    void dfs(int x,int father){
    	in[x] = ++cnt;
    	int pos = lower_bound(vec.begin(),vec.end(),a[x]) - vec.begin();
    	res[x] = query(1,1,unum,pos+1,unum); //当前(除了以x为根的子树外)  全局的pos+1~最大值的逆序对 
    	update(1,1,unum,pos,1);
    	for(int i=0;i<g[x].size();i++){
    		int v = g[x][i];
    		if(v != father){
    			dfs(v,x);
    		}
    	}
    	update(1,1,unum,pos,-1);
    	out[x] = cnt;
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++) {
    		scanf("%d",&a[i]);
    		vec.push_back(a[i]);
    	}
    	for(int i=1;i<=n-1;i++){
    		int fa;
    		scanf("%d",&fa);
    		g[fa].push_back(i+1);
    	}
    	num = n;
    	for(int i=1;i<=m;i++){
    		scanf("%d%d",&op[i].type,&op[i].u);
    		if(op[i].type == 1){
    			scanf("%d",&op[i].x);
    			a[++num] = op[i].x;
    			vec.push_back(a[num]);
    			g[op[i].u].push_back(num);
    		}
    	}
    	//离散化 
    	vec.push_back(-inf);
    	sort(vec.begin(),vec.end());
    	vec.erase(unique(vec.begin(),vec.end()),vec.end());
    	unum = vec.size() - 1;
    	//建立线段树 
    	build(1,1,unum);
    	dfs(1,0); //dfs上插入结点 
    	//建立树状数组 维护区间和 
    	for(int i=1;i<=n;i++){
    		add(in[i],res[i]); //先把原始树上的点插入好
    	}
    	int id = n;
    	//m个操作 离线加点 
    	for(int i=1;i<=m;i++){
    		if(op[i].type == 1){
    			id++;
    			add(in[id],res[id]);
    		}else{
    			//子树区间对应时间戳: in[op[i].u] ~ out[op[i].u]
    			printf("%lld
    ",ask(cnt) - rangeAsk(in[op[i].u],out[op[i].u]));
    		}
    	}
    	return 0;
    } 
    
  • 相关阅读:
    .NET、C#和ASP.NET,ASP.NET MVC 四者之间的区别
    Row_Number()显示行号
    iframe高度宽度自适应(转)
    Windows CMD命令大全(转)
    wndows系统命令总结
    删除ORACLE目录OCI.dll文件无法删除 (转)
    IE8兼容性调试及IE 8 css hack
    backbone学习笔记:视图(View)
    js中toFixed() 的使用(转)
    backbone学习笔记:集合(Collection)
  • 原文地址:https://www.cnblogs.com/fisherss/p/12233139.html
Copyright © 2011-2022 走看看