【题目描述】
幼儿园里的小朋友们正在和老师玩一个有趣的游戏。小朋友们被分成了 N 小组,初始时第i小组的人数为 ai 。老师先选定一些编号在[l,r]内的小组,然后开始与这段连续的小组的一次游戏。游戏是这样进行的:每次游戏分成若干轮,每一轮老师转过身去,[l,r]中的小组的小朋友们需要在这时通过选举选出 3 个来自不同小组的小朋友上前领老师的糖果, 并且为了体现公平,以下情形被视为犯规:某一轮选出了 3 个人领糖果,而后来这 3 个人又在同一 轮被选举出来领糖果。现在老师会不时地问你:假如他与编号在[l,r]内的小组做一次游戏,则最多能玩多少轮。小组不是固定的,人数会不时发生变化。
【输入描述】
第一行两个整数 N, M ,分别为小组数目和事件数目。
第二行 N 个整数表示 ai 。
接下来 M 行,每行描述一个事件。事件为以下两类之一(第一个数为事件类型) :
1 l r 表示询问
2 id p 表示第id 个小组的人数变成 p 。
【输出描述】
对于每一个询问操作,输出一行一个整数为答案。因为答案可能很大,只需要输出其对 1e9 + 7 取模后的结果即可。
【思路】
首先我们假设只有三个组,那么答案应该是三个组的人数的乘积,那么推广到任意大小区间:
a
n
s
[
l
,
r
]
=
∑
a
[
i
]
∗
a
[
j
]
∗
a
[
k
]
(
l
<
=
i
,
j
,
k
<
=
r
且
i
,
j
,
k
不
等
)
ans[l,r]=\sum a[i]*a[j]*a[k](l<=i,j,k<=r且i,j,k不等)
ans[l,r]=∑a[i]∗a[j]∗a[k](l<=i,j,k<=r且i,j,k不等)
对于一个左区间和一个右区间的信息,我们思考一下如何合并这两个区间。那么大区间的答案应该包括以下几个部分:第一,三个数全部在左或右区间,直接把左右区间的答案累加进大区间的值即可;第二,两个区间分别有1个数,2个数。此时稍加 分析并进行简单的 数学推导可得,答案是一个区间的和乘另一个区间的两两之间积的和。那么问题只剩下如何维护一个区间每个数两两之间积的和了。此时再分两种情况,当两个数都在左或右区间时,将两个区间的值累加即可,或者两个区间分别有一个数,此时就把两边区间的和相乘即可。那么我们只 需要维护三个值——区间的和,两两之间积的和,区间内任意三个数之间积的和就可以完成本题了。
代码:
#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#define re register
#define lc (p<<1)
#define rc (p<<1|1)
#define len(p) (t[p].r-t[p].l+1)
using namespace std;
long long n,m,a[100001],b,c,x;
struct node{
long long l,r;
long long sum1;
long long sum2;
long long sum3;
}t[400001];
long long mod=1e9+7;
inline node merge(node a,node b)
{
node ans;
ans.l=a.l;
ans.r=b.r;
ans.sum1=a.sum1+b.sum1;ans.sum1%=mod;
ans.sum3=a.sum1*b.sum2%mod+a.sum2*b.sum1%mod+a.sum3+b.sum3;ans.sum3%=mod;
ans.sum2=a.sum2+b.sum2+a.sum1*b.sum1%mod;ans.sum2%=mod;
return ans;
}
void build(long long p,long long l,long long r)
{
t[p].l=l;
t[p].r=r;
if(l==r)
{
t[p].sum1=a[l];
return;
}
long long mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
t[p]=merge(t[lc],t[rc]);
}
void change(long long p,long long k,long long v)
{
if(t[p].l==t[p].r)
{
t[p].sum1=v;
return;
}
long long mid=(t[p].l+t[p].r)>>1;
if(k<=mid)change(lc,k,v);
if(k>mid)change(rc,k,v);
t[p]=merge(t[lc],t[rc]);
}
inline node ask(long long p,long long l, long long r)
{
if(t[p].l>=l&&t[p].r<=r)
return t[p];
long long mid=(t[p].l+t[p].r)>>1;
if(l<=mid&&r<=mid)return ask(lc,l,r);
if(r>mid&&l>mid)return ask(rc,l,r);
return merge(ask(lc,l,r),ask(rc,l,r));
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int re i=1;i<=n;i++)
scanf("%lld",&a[i]);
build(1,1,n);
for(int re i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&x,&b,&c);
if(x==1)
printf("%lld\n",ask(1,b,c).sum3);
else
change(1,b,c);
}
}
是不是很弱智的题呀?