题目大意:
对于一个序列,定义从l,r一段的g值为这一段连通块数的个数。
如:
[1,1,1]值为1,1单独构成一块。
[5,7]为2,5和7分别为1块
[1,7,7,7,7,7,7,7,9,9,9,9,9,9,9,9,9]为3,分别分成[1];[7,7,7,7,7,7,7],[9,9,9,9,9,9,9,9,9]三块。
给定一个长度为n的序列,m次操作,每一次询问将a[i]改为x,输出每一次操作后整个序列所有段的g值,即:
数据:1≤n,m≤10e5
思路:
暴力显然行不通。然而一个序列总的段数可以轻易算出来为:n*(n+1)/2;每一段对答案的贡献至少为1,然后遍历序列,可以发现,如果相邻两个数相同,包含这两个数的区间并不会生成新的贡献;如果不同,包含两个数的所有区间都会产生1个贡献。总共就会增加i*(n-i)的贡献。
首先预处理出原来的答案。对于每一次修改,只需要判断修改位置的相邻元素,如果从不等变成相等了,就要在原来的基础上减去他们对区间的贡献,否则补上贡献。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long ll;
int a[N];
int main()
{
int n,m;
cin>>n>>m;
ll ans=1ll*n*(n+1)/2;
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
if(a[i]!=a[i-1]) ans+=1ll*(i-1)*(n-i+1);
}
while(m--){
int p,x;
scanf("%d%d",&p,&x);
if(a[p]==x){
printf("%lld\n",ans);
continue;
}
if(p>1&&a[p]==a[p-1]&&x!=a[p-1]) ans+=1ll*(p-1)*(n-p+1);
if(p<n&&a[p]==a[p+1]&&x!=a[p+1]) ans+=1ll*(p)*(n-p);
if(p>1&&a[p]!=a[p-1]&&x==a[p-1]) ans-=1ll*(p-1)*(n-p+1);
if(p<n&&a[p]!=a[p+1]&&x==a[p+1]) ans-=1ll*(p)*(n-p);
printf("%lld\n",ans);
a[p]=x;
}
return 0;
}