【HDU5751 BestCoder Round 84E】【FFT + 线段树求最值】Eades 最大数出现次数为[1~n]的区间个数

来源:互联网 发布:搜索关键词淘宝 编辑:程序博客网 时间:2024/05/16 00:59

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=lr[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想要知道有多少整数对llrr (l \le r)(lr)满足f(l,r)=kf(l,r)=k.
输入描述
输入包含多组数据, 第一行包含一个整数TT表示测试数据组数. 对于每组数据:第一行包含一个整数nn (1 \le n \le 60000)(1n60000)表示序列的长度. 第二行包含nn个整数a_1,a_2,...,a_na1,a2,...,an (1 \le a_i \le n)(1ain).
输出描述
对于每组数据, 输出一个整数S = \displaystyle\sum_{k=1}^{n}k \oplus z_kS=k=1nkzk, 其中z_kzk表示满足f(l,r)=kf(l,r)=k的数对llrr的个数, \oplus是异或位运算操作.
输入样例
331 2 341 1 1 161 2 2 1 1 2
输出样例
121236

#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,rtypedef 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[N];vector<int>p[N];int mx[1 << 17];LL ans[N];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)【数据】样例解释——61 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*/


0 0
原创粉丝点击