zoukankan      html  css  js  c++  java
  • JZOJ 4270.【NOIP2015模拟10.27】魔道研究

    魔道研究

    题面

    思路

    简单的想,就是在 \(T\) 个可重集合每个中选出 \(k\) 个最大的数组成新的可重集合,其中 \(k\) 为其编号
    然后在新的集合中选前 \(n\) 大的数,求其和

    考虑开 \(T + 1\) 个权值线段树,维护对应的 \(T\) 个可重集合和答案可能在的第 \(T + 1\) 个代表新的集合的线段树
    由于空间限制,我们需要动态开点(其实动态开点很简单,线段树二分下去时,遇到一个空节点再使用它。如此一来,在只需开可能使用的节点数)
    然后维护区间个数,区间和(注意一个点可能有多个数)
    因为是动态开点,所以再记录它的左、右子树的编号

    对于 \(B\) 操作,我们直接在根为 \(t\) 的线段树中加入,然后考虑它能不能进入第 \(T + 1\) 棵线段树成为可能的答案。
    即查它在第 \(t\) 棵线段树中的从大到小的排名(其实就是求第 \(t\) 棵线段树中 \(p\) 到上限的个数)和)。
    如果它的排名 \(\leq t\) ,则可能加入第 \(T + 1\) 个线段树。加入后把现在排名为 \(t+1\) 的数从第 \(T + 1\) 棵线段树中删去(即原先的排名为 \(t\) 的数,它在第 \(T + 1\) 棵线段树中),当然,如果有的话。

    对于 \(R\) 操作,就是 \(B\) 操作的逆操作,具体见代码。

    \(Code\)

    #include<cstdio>
    #include<iostream>
    using namespace std;
    typedef long long LL;
    
    const int N = 3e5 , Len = 1e9;
    int n , m , tot , len = N + 1;
    LL tr[18000005][5];
    
    inline void New(int t , int x){if (!tr[t][x]) tr[t][x] = ++len;}
    
    inline void update(int t , int l , int r , int p , int v)
    {
    	tr[t][2] += (LL)v;
    	tr[t][3] += (LL)p * v;
    	if (l == r) return;
    	int mid = (l + r) >> 1;
    	if (p <= mid) 
    	{
    		if (!tr[t][0]) New(t , 0);
    		update(tr[t][0] , l , mid , p , v);
    	}
    	else{
    		if (!tr[t][1]) New(t , 1);
    		update(tr[t][1] , mid + 1 , r , p , v);
    	}
    }
    
    inline int findk(int t , int l , int r , int x , int y)
    {
    	if (l >= x && r <= y) return (int)tr[t][2];
    	int res = 0 , mid = (l + r) >> 1;
    	if (x <= mid && tr[tr[t][0]][2]) res += findk(tr[t][0] , l , mid , x , y);
    	if (y > mid && tr[tr[t][1]][2]) res += findk(tr[t][1] , mid + 1 , r , x , y);
    	return res;  
    }
    
    inline int kfind(int t , int l , int r , int k)
    {
    	if (l == r) return k <= tr[t][2] ? l : 0;
    	int mid = (l + r) >> 1;
    	if (tr[tr[t][1]][2] < k) return kfind(tr[t][0] , l , mid , k - tr[tr[t][1]][2]);
    	else return kfind(tr[t][1] , mid + 1 , r , k);
    }
    
    inline LL query(int t , int l , int r , int k)
    {
    	if (l == r) return 1LL * min(1LL * k , tr[t][2]) * l;
    	int mid = (l + r) >> 1;
    	LL res = 0;
    	if (tr[tr[t][1]][2] <= k)
    	{
    		res += tr[tr[t][1]][3];
    		if (tr[tr[t][0]][2] && k > tr[tr[t][1]][2])
    			res += query(tr[t][0] , l , mid , k - tr[tr[t][1]][2]);
    	}
    	else{
    		if (tr[tr[t][1]][2]) res += query(tr[t][1] , mid + 1 , r , k);
    	}
    	return res;
    }
    
    int main()
    {
    	freopen("grimoire.in" , "r" , stdin);
    	freopen("grimoire.out" , "w" , stdout);
    	scanf("%d%d" , &n , &m);
    	int t , p;
    	char op[8];
    	for(register int i = 1; i <= m; i++)
    	{
    		int s1 , s2;
    		scanf("%s%d%d" , op , &t , &p);
    		if (op[0] == 'B')
    		{
    			update(t , 1 , Len , p , 1);
    			s1 = findk(t , 1 , Len , p , Len);
    			if (s1 <= t)
    			{
    				update(N + 1 , 1 , Len , p , 1);
    				s2 = kfind(t , 1 , Len , t + 1);
    				if (s2) update(N + 1 , 1 , Len , s2 , -1);
    			}
    		}
    		else{
    			s1 = findk(t , 1 , Len , p , Len);
    			update(t , 1 , Len , p , -1);
    			if (s1 <= t)
    			{
    				update(N + 1 , 1 , Len , p , -1);
    				s2 = kfind(t , 1 , Len , t);
    				if (s2) update(N + 1 , 1 , Len , s2 , 1);
    			}
    		}
    		printf("%lld\n" , query(N + 1 , 1 , Len , n));
    	}
    }
    
  • 相关阅读:
    Ubuntu adb devices :???????????? no permissions (verify udev rules) 解决方法
    ubuntu 关闭显示器的命令
    ubuntu android studio kvm
    ubuntu 14.04版本更改文件夹背景色为草绿色
    ubuntu 创建桌面快捷方式
    Ubuntu 如何更改用户密码
    ubuntu 14.04 返回到经典桌面方法
    ubuntu 信使(iptux) 创建桌面快捷方式
    Eclipse failed to get the required ADT version number from the sdk
    Eclipse '<>' operator is not allowed for source level below 1.7
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/13377115.html
Copyright © 2011-2022 走看看