codeplus2017 Yazid的新生舞会
作者:互联网
Yazid的新生舞会 codeplus2017 题解
受到 type 是 \(1\) 和 \(3\) 的启发,我们可以枚举 \(num\) 为众数,然后把每一个的答案相加。
假设我们正在计算一个众数,计算它前缀的出现次数 \(s_i\),那合法的情况要满足 \(s_r-s_l>\frac{r-l}{2}\),即 \(2s_r-r>2s_l-l\)。这可以看成逆序对问题,可以用树状数组维护,查询 \(2s_i-i-1\),插入 \(2s_i-i\)。
我们继续研究,如果 \(i\) 是第一次出现 \(num\) 的位置,\(j\) 是第二次出现的位置。那 \(k\in i\sim j-1\) 的 \(s_k\) 都相等。但 \(k\) 是递增的,所以每次查询的 \(2s_k-k-1\) 是递减的,但每次修改是 \(2s_k-k\),所以在 \(i\sim j-1\) 中的修改操作不会对本区间的查询操作有影响。
所以我们可以先统一查询 \(i\sim j-1\) 中的询问,然后统一修改。修改很容易,给 \(2s_i-i\) 到 \(2s_i-j+1\) 中的每一个加上 \(1\),用树状数组打差分标记即可。
所以主要的问题在于查询。每一个 \(ask(2s_k-k-1)\) 都是树状数组差分标记的前缀和,而我们计算 \(i\sim j-1\) 中的每一个的答案又是 \(ask\) 的前缀和,即 \(\sum\limits_{l=1}^{s_k-k-1} ask(l)\),记为 \(A(i)\),我们可以在再设 \(S(i)=\sum A(x)\),那我们求的就是 \(S(j-1)-S(i-2)\)。这样,\(S(i)\) 就是树状数组差分标记的三阶前缀和。为啥这里是i-2呢?因为逆序对!我们查询是到它-1对叭!我们要查询 \(A(t-1)\)的权值和, 其中 t 在\([i,j]\)范围内,
下面我们来求一下三阶前缀和。即 \(d\) 为原数组,\(c\) 是 \(d\) 的前缀和,\(b\) 是 \(c\) 的前缀和,\(a\) 是 \(b\) 的前缀和。那 \(a\) 就是 \(d\) 的前缀和。
\(a_n=\sum\limits_{i,j,k}d_k\)。
我们来计算对于每一个 \(d_k\) 在 \(a_n\) 中出现了多少次。
\(k=1\) 时,\(j\) 可以取 \(1\sim n\)。\(j\) 取 \(1\) 时,\(i\) 可以取 \(1\sim n\);$j $ 取 \(2\) 时,\(i\) 可以取 \(2\sim n\)。以此类推,共有 \(\frac{(n+1)n}{2}\) 次。
同理 \(k=2\) 时,共有 \(\frac{n(n-1)}{2}\) 次。
最后整合一下,\(d_k\) 出现的次数是 \(\frac{(n+2-k)(n+1-k)}{2}\)。
\(a_n=\sum\limits_{k=1}^n \frac{(n+2-k)(n+1-k)}{2}d_k\)。
把括号拆开,用树状数组维护一下 \(k\) 的每一项即可。
\(a_n=\sum\limits_{k=1}^n \frac{(n+2-k)(n+1-k)}{2}d_k=\frac{(n+2)(n+1)}{2}\sum\limits_{k=1}^nd_k-\frac{2n+3}{2}\sum\limits_{k=1}^nd_k*k+\frac{1}{2}\sum\limits_{k=1}^{n}d_k*k^2\)
这是对于一个 \(num\) 的情况,我们枚举每个 \(num\),然后按每一段进行计算,整体复杂度是 \(O(\sum size\times\log n)=O(n\log n)\) 的。
下面是 AC 代码 & 注释
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long maxn=1e6+5;
const long long inf=0x3f3f3f3f;
long long n,op,ans,c1[maxn],c2[maxn],c3[maxn];
vector<long long> a[maxn];
long long lowbit(long long x)
{
return x&(-x);
}
void add(long long x,long long nm)
{
for(long long i=x;i<=2*n+1;i+=lowbit(i))
{
c1[i]+=nm;
c2[i]+=nm*x;
c3[i]+=nm*x*x;
}
}
long long sum(long long x)
{
long long res=0;
for(long long i=x;i>=1;i-=lowbit(i))
res+=c1[i]*(x+1)*(x+2)-c2[i]*(2*x+3)+c3[i];
return res/2;
}
int main()
{
freopen("party.in","r",stdin);
freopen("party.out","w",stdout);
cin>>n>>op;
for(long long i=1;i<=n;i++)
{
long long x;
cin>>x;
a[x+1].push_back(i);
}
for(long long num=1;num<=n;num++)
{
if(a[num].empty())
continue;
a[num].push_back(n+1);
for(long long i=0;i<a[num].size();i++) //i就是出现次数
{
//y是上文第一次出现的i,x是上文的j
//这里加上n+1是偏移量,把[-n,n]平移到[1,2n+1]
long long y=2*i-(i==0?0:a[num][i-1])+n+1;
long long x=2*i-(a[num][i]-1)+n+1;
//查询 A(t-1)的权值和, 其中t在[x,y]范围内,
ans+=sum(y-1)-sum(x-2);
add(x,1);
add(y+1,-1);
}
for(long long i=0;i<a[num].size();i++) //清空树状数组
{
long long y=2*i-(i==0?0:a[num][i-1])+n+1;
long long x=2*i-(a[num][i]-1)+n+1;
add(x,-1);
add(y+1,1);
}
}
cout<<ans<<endl;
return 0;
}
标签:codeplus2017,舞会,frac,前缀,limits,2s,sum,long,Yazid 来源: https://www.cnblogs.com/zxi8-may/p/16300743.html