• 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;
    } 
    
  • 相关阅读:
    UVA 10617 Again Palindrome
    UVA 10154 Weights and Measures
    UVA 10201 Adventures in Moving Part IV
    UVA 10313 Pay the Price
    UVA 10271 Chopsticks
    Restore DB後設置指引 for maximo
    每行SQL語句加go換行
    种服务器角色所拥有的权限
    Framework X support IPV6?
    模擬DeadLock
  • 原文地址:https://www.cnblogs.com/fisherss/p/12233139.html
Copyright © 2020-2023  润新知