ZOJ 3940 Modulo Query (2016年浙江省赛E题,区间折叠 + map运用)

题目链接  2016 ZJCPC Problem E

考虑一个开区间$[0, x)$对$a_{i}$取模的过程。

$[0, x)$中小于$a_{i}$的部分不变,大于等于$a_{i}$的部分被切下来变成了$[0, x$ $mod$ $a_{i})$。

现在考虑开区间$[0, m+1)$,依次对$a_{1}, a_{2}, ..., a_{n}$取模。

考虑到一个数对$10^{5}$个数逐次取模,有效的取模至多$logm$次,那么同理,

这个区间最多分裂出$nlogm$个区间,这个过程用map实现(map自带平衡树的功能)。

于是处理完毕之后整个区间变成了。

$[0, c_{1})$,频数为$d_{1}$;

$[0, c_{2})$,频数为$d_{2}$;

$[0, c_{3})$,频数为$d_{2}$;

......

$[0, c_{k})$,频数为$d_{k}$;

对某个询问$y$,找到最大的$x$满足$c_{x} <= y$,那么该询问的答案即为 $\sum\nolimits_{i=x+1}^kd_i$

那么对询问离线从小到大处理即可。

 

#include 

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define fi		first
#define se		second

const int N   = 1e5 + 10;
const int mod = 1e9 + 7;

typedef pair  PII;

map  mp;
map  :: iterator it;

PII a[N];

int n, m, q;
int T;
int ret, ans;

int main(){

	scanf("%d", &T);
	while (T--){
		mp.clear();
		scanf("%d%d", &n, &m);

		mp[m + 1] = 1;
		rep(i, 1, n){
			int x;
			scanf("%d", &x);
			while (true){
				it = mp.upper_bound(x);
				if (it == mp.end()) break;
				mp[x] += it -> fi / x * it -> se;
				if (it -> fi % x) mp[it -> fi % x] += it -> se;
				mp.erase(it);
			}
		}

		ret = 0;
		for (auto u : mp) ret += u.se;
		scanf("%d", &q);
		rep(i, 1, q){
			scanf("%d", &a[i].fi);
			a[i].se = i;
		}

		sort(a + 1, a + q + 1);
		it = mp.begin();
		ans = 0;
		rep(i, 1, q){
			while (a[i].fi >= it -> fi){
				ret -= it -> se;
				++it;
				if (it == mp.end()) break;
			}

			if (ret == 0) break;
			ans = (ans + 1ll * ret * a[i].se) % mod;
		}
		
		printf("%d\n", ans);
	}

	return 0;
}

 

转载于:https://www.cnblogs.com/cxhscst2/p/8607986.html

你可能感兴趣的:(ZOJ 3940 Modulo Query (2016年浙江省赛E题,区间折叠 + map运用))