zoukankan      html  css  js  c++  java
  • 【BZOJ3110】[ZJOI2013]K大数查询(整体二分)

    题目:

    BZOJ3110

    分析:

    整体二分模板题……

    先明确一下题意:每个位置可以存放多个数,第一种操作是“加入 (insert) ”一个数而不是“加上 (add) ”一个数。

    首先考虑只有一次询问的情况。设询问的名次为(k),我们二分出一个答案(mid),然后遍历所有修改。建立一棵区间线段树(下标是位置的线段树),对于一个给([a,b])区间加入一个数(c)的修改,如果(cgeq mid),就给([a,b])这个区间整体加(1)。最后查询询问区间的和,即这个区间中不小于(mid)的数的数量。如果这个数量大于等于(k)则向上二分,并记录答案,否则向下二分。这样单次询问的复杂度是(O(mlog_2^2n))

    可以看出,询问时最耗时间的是遍历所有修改并维护线段树。而这个操作只与当前二分到的(mid)有关,与询问无关。因此考虑把所有询问放在一个集合中,每次把询问分为将要“向上二分”(答案在([mid+1,r]))和“向下二分”(答案在([l,mid])两个集合,并把可能对它们产生贡献的修改也分别加入这两个集合,然后分别递归下去。这就是“整体二分”。下面重点介绍如何对询问和修改分为两个集合。以下把修改和询问统称为“操作”。

    询问的分法比较显然。如同只有一个询问的情况,把当前操作集合中的修改全部插入到线段树上。如果询问区间的和大于等于询问的名次则把这个询问分到“向上二分”的集合中,否则分到“向下二分”的集合中。

    考虑修改。如果一个修改所加入的数不大于(mid),那么对于已经认定答案在([mid+1,r])的询问一定是没有贡献的,所以只需要加到向下二分的集合中;如果一个修改所加入的数大于(mid),那么对于已经认定答案在([l,mid])的询问一定是有(1)的贡献。如果把答案在([l,mid])的询问所求的名次都减去(1),则这个修改也只会对向上二分的集合中的询问有贡献,只需要加到向上二分的集合中。这样每次都把所有操作分成独立的两部分,最多分(log_2n)次。每层所有操作集合的并集刚好是原集合,所以每层的时间复杂度是(nlog_2n),总复杂度(O(nlog_2^2n))。具体流程可以参考代码。

    代码:

    并不需要真正把操作分成两个集合,只分它们的编号即可。

    #include <cstdio>
    #include <algorithm>
    #include <cctype>
    #include <cstring>
    using namespace std;
    
    namespace zyt
    {
    	template<typename T>
    	inline void read(T &x)
    	{
    		char c;
    		bool f = false;
    		x = 0;
    		do
    			c = getchar();
    		while (c != '-' && !isdigit(c));
    		if (c == '-')
    			f = true, c = getchar();
    		do
    			x = x * 10 + c - '0', c = getchar();
    		while (isdigit(c));
    		if (f)
    			x = -x;
    	}
    	template<typename T>
    	inline void write(T x)
    	{
    		static char buf[20];
    		char *pos = buf;
    		if (x < 0)
    			putchar('-'), x = -x;
    		do
    			*pos++ = x % 10 + '0';
    		while (x /= 10);
    		while (pos > buf)
    			putchar(*--pos);
    	}
    	typedef long long ll;
    	const int N = 5e4 + 10, B = 16, CHANGE = 1, QUERY = 2;
    	int n, m, id[N], ans[N];
    	struct node
    	{
    		int type, l, r;
    		ll c;
    	}opt[N];
    	namespace Segment_Tree
    	{
    		struct node
    		{
    			ll sum, tag;
    		}tree[1 << (B + 1)];
    		inline void update(const int rot)
    		{
    			tree[rot].sum = tree[rot << 1].sum + tree[rot << 1 | 1].sum;
    		}
    		inline void push_down(const int rot, const int lt, const int rt)
    		{
    			if (tree[rot].tag)
    			{
    				ll &tag = tree[rot].tag;
    				int mid = (lt + rt) >> 1;
    				tree[rot << 1].sum += tag * (mid - lt + 1);
    				tree[rot << 1].tag += tag;
    				tree[rot << 1 | 1].sum += tag * (rt - mid);
    				tree[rot << 1 | 1].tag += tag;
    				tag = 0;
    			}
    		}
    		void add(const int rot, const int lt, const int rt, const int ls, const int rs, const int x)
    		{
    			if (ls <= lt && rt <= rs)
    			{
    				tree[rot].sum += x * (rt - lt + 1), tree[rot].tag += x;
    				return;
    			}
    			int mid = (lt + rt) >> 1;
    			push_down(rot, lt, rt);
    			if (ls <= mid)
    				add(rot << 1, lt, mid, ls, rs, x);
    			if (rs > mid)
    				add(rot << 1 | 1, mid + 1, rt, ls, rs, x);
    			update(rot);
    		}
    		ll query(const int rot, const int lt, const int rt, const int ls, const int rs)
    		{
    			if (ls <= lt && rt <= rs)
    				return tree[rot].sum;
    			int mid = (lt + rt) >> 1;
    			ll ans = 0;
    			push_down(rot, lt, rt);
    			if (ls <= mid)
    				ans += query(rot << 1, lt, mid, ls, rs);
    			if (rs > mid)
    				ans += query(rot << 1 | 1, mid + 1, rt, ls, rs);
    			return ans;
    		}
    	}
    	void solve(const int idl, const int idr, const int l, const int r)
    	{//As for any question, determine
    	//wheather there are more than opt[i].c numbers greater than mid or not
    	//If so, ans[i] will be greater than mid. Otherwise less.
    		using Segment_Tree::query;
    		using Segment_Tree::add;
    		static int newl[N], newr[N];
    		if (l == r)
    		{
    			for (int i = idl; i <= idr; i++)
    				if (opt[id[i]].type == QUERY)
    					ans[id[i]] = l;
    			return;
    		}
    		int lcnt = 0, rcnt = 0;
    		int mid = (l + r) >> 1;
    		for (int i = idl; i <= idr; i++)
    		{
    			if (opt[id[i]].type == CHANGE)
    			{
    				if (opt[id[i]].c <= mid)
    					newl[lcnt++] = id[i];
    				else
    				{
    					add(1, 1, n, opt[id[i]].l, opt[id[i]].r, 1);
    					query(1, 1, n, 100, 100);
    					newr[rcnt++] = id[i];
    				}
    			}
    			else
    			{
    				ll tmp = query(1, 1, n, opt[id[i]].l, opt[id[i]].r);
    				if (tmp < opt[id[i]].c)
    					newl[lcnt++] = id[i], opt[id[i]].c -= tmp;
    				else
    					newr[rcnt++] = id[i];
    			}
    		}
    		for (int i = idl; i <= idr; i++)
    			if (opt[id[i]].type == CHANGE && opt[id[i]].c > mid)
    				add(1, 1, n, opt[id[i]].l, opt[id[i]].r, -1);
    		memcpy(id + idl, newl, sizeof(int[lcnt]));
    		memcpy(id + idl + lcnt, newr, sizeof(int[rcnt]));
    		if (lcnt)
    			solve(idl, idl + lcnt - 1, l, mid);
    		if (rcnt)
    			solve(idl + lcnt, idr, mid + 1, r);
    	}
    	int work()
    	{
    		read(n), read(m);
    		for (int i = 1; i <= m; i++)
    		{
    			read(opt[i].type);
    			read(opt[i].l);
    			read(opt[i].r);
    			read(opt[i].c);
    			id[i] = i;
    		}
    		solve(1, m, -N, N);
    		for (int i = 1; i <= m; i++)
    			if (opt[i].type == QUERY)
    				write(ans[i]), putchar('
    ');
    		return 0;
    	}
    }
    int main()
    {
    	freopen("3110.in", "r", stdin);
    	freopen("3110.out", "w", stdout);
    	return zyt::work();
    }
    
  • 相关阅读:
    [OpenJudge90][序列DP+乱搞]滑雪
    [OpenJudge8786][暴力DP]方格取数
    [OpenJudge8782][划分DP]乘积最大
    [OpenJudge8471][划分DP]切割回文
    [OpenJudge8462][序列DP]大盗阿福
    【棋盘DP】【OpenJudge7614】最低通行费
    【OpenJudge8464】【序列DP】股票买卖
    bzoj1674: [Usaco2005]Part Acquisition 裸dijkstra
    bzoj3040 最短路+配对堆优化
    poj1330|bzoj3732|noip2013 货车运输 kruskal+倍增lca
  • 原文地址:https://www.cnblogs.com/zyt1253679098/p/10012800.html
Copyright © 2011-2022 走看看