其他分享
首页 > 其他分享> > codeplus2017 Yazid的新生舞会

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