UVa 1406 - A Sequence of Numbers
Published on 2016-01-07描述
给定一个长度为 的序列,每个数的范围都是 ,现在有两种操作:
C T
:给序列里的每一个数加上一个数 ,如果某个数超过了 ,将其对 取余数。Q T
:给定一个数 ,查询序列里有多少个数的二进制位的第 位是 。
现在给出 个查询,为了方便起见,请你输出所有 Q T
操作结果的和 。
样例输入
3 1 2 4 Q 1 Q 2 C 1 Q 1 Q 2 E -1
样例输出
Case 1: 5
附加输入
17 0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 Q 0 C 1 Q 0 E 4 0 101 563 555 Q 1 Q 5 C 1 Q 1 C 888 Q 7 E -1
附加输出
Case 1: 17 Case 2: 9
分析
乍一看线段树搞一搞就行,但很大的问题就是查询是针对二进制位的,所以光打 标记是无法知道数的二进制位的,于是得想办法。
由于一口气查询所有数的某一二进制位是不是 ,所以我们考虑维护 个树状数组,第 个树状数组维护所有数的二进制位 位的和。每个树状数组的位置 记录有几个数和为 。
举个例子,对于数 的二进制表示(仅给出前 8 位,最右边为最低位):,那么第 位对应的和为 ,第 位则对应 。对应第 个树状数组的 位置的值为
如果遇到一个查询,记录之前所有增加操作的和 ,注意每次对 取余。如果查询第 位,那么第 位的值显然与大于 位没有一点关系,所以我们可以只考虑第 个树状数组, 值也可以只要 位。
接下来就是讨论 位的数可取哪些值,加上 值后仍为第 位仍然为 ,很简单,拿纸算一下就行了,有几种情况(如果你只想要答案,懒得算就看下面)。
如果 的第 位是 ,要么数 的第 位是 且之前位加上来没有进位,要么数 的第 位是 且之前位加上来有进位。
如果 的第 位是 ,要么数 的第 位是 且之前位加上来没有进位,要么数 的第 位是 且之前位加上来有进位。
综合一下就是(设 为 的二进制第 位):
接下来树状数组求和就行,这题构造很巧妙,细节参见代码。
代码
// Created by Sengxian on 1/7/16. // Copyright (c) 2015年 Sengxian. All rights reserved. // UVa 1406 树状数组 #include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <vector> #include <climits> #include <cassert> using namespace std; const int maxn = 100000 + 3, maxBit = 17; //Fenwick Tree 0 - indexed struct Fenwick { vector<int> c; int n; //init [0, n) void init(int n) { this -> n = n; c = vector<int>(n + 1, 0); } static inline int lowbit(int x) { return x & -x; } //0 - indexed; void add(int x, int d) { x++; while(x <= n) { c[x] += d; x += lowbit(x); } } //sum of [0, x) int sum(int x) { int ret = 0; while(x > 0) { ret += c[x]; x -= lowbit(x); } return ret; } //sum of [l, r) int Sum(int l, int r) { return sum(r) - sum(l); } }Bits[maxBit]; int n; int main() { int caseNum = 0; while(~scanf("%d", &n) && ~n) { long long ans = 0; int sum = 0; for(int i = 0; i < maxBit; ++i) Bits[i].init(1 << (i + 1)); for(int i = 0; i < n; ++i) { int x, tot = 0; scanf("%d", &x); for(int j = 0; j < maxBit; ++j) { if((x >> j) & 1) tot += 1 << j; Bits[j].add(tot, 1); } } char ch; while(getchar(), ch = getchar(), ch != 'E') if(ch == 'Q') { int x; scanf("%d", &x); int t = sum; sum %= 1 << (x + 1); if((sum >> x) & 1) { //第 x 位为 1 sum -= 1 << x; ans += Bits[x].Sum(0, (1 << x) - sum); ans += Bits[x].Sum((1 << x + 1) - sum, 1 << x + 1); }else ans += Bits[x].Sum((1 << x) - sum, (1 << x + 1) - sum); sum = t; }else if(ch == 'C') { int a = 0; scanf("%d", &a); sum += a, sum %= 1 << 16; } else assert(false); printf("Case %d: %lld\n", ++caseNum, ans); } return 0; }