Codeforces Round #816(div 2) C. Monoblock

题目连接:Problem - C - Codeforces

题目大意:

对于一个序列,定义从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值,即:

Codeforces Round #816(div 2) C. Monoblock_第1张图片

数据:1≤n,m≤10e5

思路:

暴力显然行不通。然而一个序列总的段数可以轻易算出来为:n*(n+1)/2;每一段对答案的贡献至少为1,然后遍历序列,可以发现,如果相邻两个数相同,包含这两个数的区间并不会生成新的贡献;如果不同,包含两个数的所有区间都会产生1个贡献。总共就会增加i*(n-i)的贡献。Codeforces Round #816(div 2) C. Monoblock_第2张图片

 首先预处理出原来的答案。对于每一次修改,只需要判断修改位置的相邻元素,如果从不等变成相等了,就要在原来的基础上减去他们对区间的贡献,否则补上贡献。

代码:

#include
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(p1&&a[p]!=a[p-1]&&x==a[p-1]) ans-=1ll*(p-1)*(n-p+1);
		if(p

 

你可能感兴趣的:(codeforces刷题,codeforces,思维题)