【HDU5751 BestCoder Round 84E】【FFT + 线段树求最值】Eades 最大数出现次数为[1~n]的区间个数
2016-07-25 09:36
633 查看
Eades
Accepts: 1
Submissions: 11
Time Limit: 12000/6000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
Peter有一个序列a_1, a_2, ..., a_na1,a2,...,an. 定义g(l,r)g(l,r)表示子序列a_{l},a_{l+1},...,a_{r}al,al+1,...,ar的最大值, f(l,r)=\displaystyle\sum_{i=l}^{r}[a_i = g(l,r)]f(l,r)=i=l∑r[ai=g(l,r)]. 注意[\text{condition}] = 1[condition]=1当且仅当\text{condition}condition是true, 否则[\text{condition}] = 0[condition]=0. 对于每个整数k \in \{1, 2, ..., n\}k∈{1,2,...,n}, Peter想要知道有多少整数对ll和rr (l \le r)(l≤r)满足f(l,r)=kf(l,r)=k.
输入描述
输入包含多组数据, 第一行包含一个整数TT表示测试数据组数. 对于每组数据: 第一行包含一个整数nn (1 \le n \le 60000)(1≤n≤60000)表示序列的长度. 第二行包含nn个整数a_1,a_2,...,a_na1,a2,...,an (1 \le a_i \le n)(1≤ai≤n).
输出描述
对于每组数据, 输出一个整数S = \displaystyle\sum_{k=1}^{n}k \oplus z_kS=k=1∑nk⊕zk, 其中z_kzk表示满足f(l,r)=kf(l,r)=k的数对ll和rr的个数, \oplus⊕是异或位运算操作.
输入样例
3 3 1 2 3 4 1 1 1 1 6 1 2 2 1 1 2
输出样例
12 12 36
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 #define lson o<<1,l,mid #define rson o<<1|1,mid+1,r typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; } const int N = 60010, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f; int casenum, casei; int n; int a ; vector<int>p ; int mx[1 << 17]; LL ans ; void build(int o, int l, int r) { if (l == r) { mx[o] = a[l]; return; } int mid = (l + r) >> 1; build(lson); build(rson); mx[o] = max(mx[ls], mx[rs]); } int getmx(int o, int l, int r, int L, int R) { if (l >= L&&r <= R)return mx[o]; int mid = (l + r) >> 1; int ret = 0; if (L <= mid)gmax(ret, getmx(lson, L, R)); if (R > mid)gmax(ret, getmx(rson, L, R)); return ret; } const double PI = acos(-1.0); struct Complex { double r, i; Complex(double r = 0, double i = 0) : r(r), i(i) {} Complex operator + (const Complex& t) const { return Complex(r + t.r, i + t.i); } Complex operator - (const Complex& t) const { return Complex(r - t.r, i - t.i); } Complex operator * (const Complex& t) const { return Complex(r * t.r - i * t.i, r * t.i + i * t.r); } }X[1 << 17], Y[1 << 17]; void FFT(Complex y[], int n, int rev)//rev == 1时为DFT,rev == -1时为IDFT { for (int i = 1, j, k, t; i < n; ++i) { for (j = 0, k = n >> 1, t = i; k; k >>= 1, t >>= 1) j = j << 1 | t & 1; if (i < j) swap(y[i], y[j]); } for (int s = 2, ds = 1; s <= n; ds = s, s <<= 1) { Complex wn(cos(rev * 2 * PI / s), sin(rev * 2 * PI / s)); for (int k = 0; k < n; k += s) { Complex w(1, 0), t; for (int i = k; i < k + ds; ++i) { y[i + ds] = y[i] - (t = w * y[i + ds]); y[i] = y[i] + t; w = w * wn; } } } if (rev == -1) for (int i = 0; i < n; ++i) y[i].r /= n; } void solve(int l, int r) { if (l > r)return; if (l == r) { ++ans[1]; return; } int v = getmx(1, 1, n, l, r); int lft = lower_bound(p[v].begin(), p[v].end(), l) - p[v].begin(); int rgt = upper_bound(p[v].begin(), p[v].end(), r) - p[v].begin() - 1; vector<int>pos; pos.push_back(l - 1); for (int i = lft; i <= rgt; ++i)pos.push_back(p[v][i]); pos.push_back(r + 1); int num = rgt - lft + 2; for (int i = 0; i < num; ++i)X[i] = { (double)pos[i + 1] - pos[i], 0 }; for (int i = 0; i < num; ++i)Y[i] = X[num - 1 - i]; int len = 1; while (len < num + num)len <<= 1; for (int i = num; i < len; ++i)X[i] = Y[i] = { 0,0 }; FFT(X, len, 1); FFT(Y, len, 1); for (int i = 0; i < len; ++i)Y[i] = X[i] * Y[i]; FFT(Y, len, -1); for (int i = 0; i < num - 1; ++i)ans[num - 1 - i] += (Y[i].r + 0.5); for (int i = 0; i < num; ++i) { solve(pos[i] + 1, pos[i + 1] - 1); } } int main() { scanf("%d", &casenum); for (casei = 1; casei <= casenum; ++casei) { scanf("%d", &n); for (int i = 1; i <= n; ++i) { p[i].clear(); ans[i] = 0; } for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); p[a[i]].push_back(i); } build(1, 1, n); solve(1, n); LL ANS = 0; for (int i = 1; i <= n; ++i)ANS += ans[i] ^ i; printf("%lld\n", ANS); } return 0; } /* 【trick&&吐槽】 1,注意尽量不要让全局变量数组设计迭代,会重复共享而出错的! 2,注意FFT的len要从1开始倍增,这样才保证其为2的幂次 【题意】 有一个长度为n(60000)的序列a[]。 我们想要知道——有多少个区间最大值数的出现数量恰好为k次(k∈[1,n]) 输出:∑(ans[k]^k) k ∈[1,n] 【类型】 线段树 + fft 【分析】 这道题,我们首先思考枚举最大值(这个思路很常见,因为一共只有n个最大值,同时,我们在知道最大值的条件下,更容易算出其对答案的贡献) 我们如何求出最大值呢? 可以写一棵线段树实现。 我们知道最大值之后,如何算出有多少个区间包含这个最大值呢? 我们可以通过查询出所有最大值点来实现。 具体而言,就是,我们把权值为x的所有数,都push_back入p[x]的vector中,存一下权值为x的数的所有位置。 然后进入一个solve(l, r)的递归函数 所谓solve(l, r),就是—— 在区间边界不能<l 且 不能 >r(即区间左右界被局限在[l,r]范围内,否则就会打破最大值限制)的条件下, 我们思考该区间内部的最大值,对答案的贡献。 用线段树可以求出该区间的最大值,设为v 然后通过二分查找,查询出在[l,r]范围内,权值为v的所有数。 接下来,我们发现,对于ans[k],我们就要找到该区间范围内第i个v,和第i+k-1个v,然后—— 假设i左侧有lft个可以不延展到v[i-1]的位点 假设i+k-1右侧有rgt个可以不延展到v[i+k]的位点, 那么,对于ans[k],我们就获得了lft*rgt的答案贡献。 然而,我们不能暴力算,暴力算的复杂度为O(n^2) 我们需要借助fft的力量。 我们求出,每个区间最大值的点,在不延展到前一个区间最大值点的条件下,其可以向左延展多少个数,记做c[] 显然,在我们求出区间内的所有数之后,比如区间内,p[v][lft]~p[v][rgt]都是权值为v的点位 那么c[0]=p[v][lft]-l+1, c[num]=r-p[v][rgt]+1, c[i]=p[v][x]-p[v][x-1] 那么,对于ans[k],我们所获得的贡献是∑c[i]*c[i+k]。 而这个所对应的,就相当于是卷积FFT。 c[0] c[1] c[2] c[3] c[4] c[0] c[1] c[2] c[3] c[4] 它们做FFT之后,我们可以得到—— (c[0] * c[0]) (c[0] * c[1] + c[1] * c[0]) (c[0] * c[2] + c[1] * c[1] + c[2] *c[1]) (c[0] * c[3] + c[1] * c[2] + c[2] * c[1] + c[3] * c[0]) (c[0] * c[4] + c[1] * c[3] + c[2] * c[2] + c[3] * c[1] + c[4] * c[0]) (c[1] * c[4] + c[2] * c[3] + c[3] * c[2] + c[4] * c[1]) (c[2] * c[4] + c[3] * c[3] + c[4] * c[2]) (c[3] * c[4] + c[4] * c[3]) (c[4] * c[4]) 做一下对称反转 c[0] c[1] c[2] c[3] c[4] c[4] c[3] c[2] c[1] c[0] 它们做FFT之后,我们可以得到—— (c[0] * c[4]) ->ans[4] (c[0] * c[3] + c[1] * c[4]) ->ans[3] (c[0] * c[2] + c[1] * c[3] + c[2] *c[4]) ->ans[2] (c[0] * c[1] + c[1] * c[2] + c[2] * c[3] + c[3] * c[4]) ->ans[1] (c[0] * c[0] + c[1] * c[1] + c[2] * c[2] + c[3] * c[3] + c[4] * c[4]) (c[1] * c[0] + c[2] * c[1] + c[3] * c[2] + c[4] * c[3]) ->ans[1] (c[2] * c[0] + c[3] * c[1] + c[4] * c[2]) ->ans[2] (c[3] * c[0] + c[4] * c[1]) ->ans[3] (c[4] * c[0]) ->ans[4] 也就生成了对ans[4],ans[3],ans[2],ans[1]...的贡献。(我们只要拿一半统计、计数即可) 【时间复杂度&&优化】 O(nlogn) 复杂度如何算呢? 每个点最多参与一次fft,所以复杂度大概为O(nlogn) 【数据】 样例解释—— 6 1 2 2 1 1 2 最大值为1时: 最大值个数为1、2的区间个数分别为:3 1 最大值为2时: 最大值个数为1、2、3的区间个数分别为:(1+1+3+1+1+1)(3+3+1)(2)=(8)(7)(2) 总区间个数为21 答案为(11)(8)(2)(0)(0)(0) =10 + 10 + 1 + 4 + 5 + 6=36 */
相关文章推荐
- HDU 1754 I Hate It
- 【HDU5565 BestCoder Round 62 (div1)C】【STL or 二分答案 or 计数排序】Clarke and baton n个人减肥m次求最后异或值
- 【HDU5564 BestCoder Round 62 (div1)B】【DP转矩阵快速幂】Clarke and digits 长度在[l,r]范围内7倍数数个数要求相邻两位不为K
- 【HDU5544 2015CCPC 南阳国赛E】【树上dfs找本质不同环 高斯消元 时间戳优化】Ba Gua Zhen 连通图上最大异或环
- 【HDU5222 2015赛码冠军杯I】【并查集找双连通 + tarjan求强连通】Exploration 双向边只能走一边是否图上存在环
- 【HDU5188 BestCoder Round 33C】【贪心排序+DP】zhx and contest 考试不被怀疑作弊条件下达到至少m分的最少时间
- 【HDU4560 2013西山居复赛D】【二分答案+网络流拆点】我是歌手 安排演唱会_每人歌不同_每场歌不同_人歌匹配一次
- 【HDU5411 2015 Multi-University Training Contest 10F】【矩阵快速幂 加一行构造法】CRB and Puzzle 矩阵的1次方到n次方的数值和
- 【HDU5570 BestCoder Round 63 (div1)C】【期望DP 公式化简】balls n种求m种颜色,同颜色球数为x贡献为x方 求期望
- 【HDU5569 BestCoder Round 63 (div1)B】【DP】matrix 向右走向下走最大乘积和
- 【HDU5568 BestCoder Round 63 (div1)A】【DP java高精度】sequence2 长度恰好为m的LIS数
- 【Codeforces Round 333 (Div 2)D】【线段树 or ST-RMQ 初始化78msAC】Lipshitz Sequence 若干区间询问所有子区间的答案和
- 【HDU5579 2015上海赛区G】【超级大讨论】Game of Arrays a[]+b[]+c[]有些位置可以减一,状态是否可能达成
- 【HDU5583 2015上海赛区L】【找规律 正难则反】LCM Walk 目标状态(x,y)哪些点走公倍数能走到它
- 【HDU5573 2015上海赛区B】【构造 二进制思想】Binary Tree 二叉树上走m层加减数使得最后权值恰为n
- 【HDU5583 2015上海赛区K】【暴力合并】Kingdom of Black and White 连续01串权值贡献为len^2最多改变一次最大权值
- 【HDU5578 2015上海赛区F】【水题】Friendship of Frog 最近的两个相同字符的距离是多少
- 【HDU5586 BestCoder Round 64 (div1)A】【贪心 最大连续子串】Sum 区间函数值变换使得数列权值和最大
- 【HDU5587 BestCoder Round 64 (div1)B】【迭代 前缀和思想】Array 前m个数的二进制中共有多少个1
- 【Educational Codeforces Round 2E】【STL-map 启发式合并 or 线段树动态开节点 】Lomsat gelral 一棵树每点一个颜色问每个节点子树的颜色众数之和