2021NOI 省选训练赛day1T1 A. light

2021NOI 省选训练赛day1T1 A. light

Problem

有一排\(n\)个灯,每个灯有一个颜色,用\(1\)\(m\)表示。一开始所有灯都是关着的。

\(q\)次操作,每次改变某种颜色的所有灯的状态。每次操作后你需要输出开着的灯组成的极长连续子段的数量。

Subtask 1 (13pts):\(n,q\le5000\)

Subtask 2 (35pts):\(m\le100\)

Subtask 3 (52pts):无特殊限制。

对于全部数据,\(1≤n,q\le 10^5\)\(1\le m\le n\)

Solution

这道题的思考过程分为两个部分。

  1. 暴力处理。
  2. 对于要修改的颜色的灯的数量进行根号分治。

暴力:只有当要修改的灯的两侧的灯的亮灭情况相同时,才会对答案造成影响(\(\pm1\))

在整个序列上,结论就是连续段数等于开着的灯数减去相邻两个都开着的灯对数

在这里也可以得出:相邻的两种颜色对答案没有任何的影响。所以预先把相邻的同色灯缩点。

但是这样每修改一次,都需要到对应颜色的灯上统计答案,时间复杂度为\(O(n)\)(比如缩点之后的序列为1,2,1,2,1,2,1...,一次修改仍然有\(\frac n2\)次操作)。

\(k=\sqrt n\),如果只是对出现次数小于\(k\)的颜色进行暴力枚举其位置修改,时间复杂度为\(O(\sqrt n)\)当然是没有问题的。对于出现次数大于\(k\)的颜色呢,我们维护与这种颜色相邻的开着的灯的数量(记为\(f_{i,j}\)表示一种出现次数大于\(k\)的颜色\(j\)的旁边有多少个颜色为\(i\)的灯泡)。每当我们改变一种出现次数大于\(k\)的颜色是时,使用这个算答案;修改一种出现次数小于\(k\)的颜色时就暴力枚举其位置并维护好\(f_{i,j}\)

Code

/**************************************************************
 * Problem: A. light-Revision
 * Author: Vanilla_chan
 * Date: 20210309
**************************************************************/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<limits.h>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#ifdef TH
#define debug printf("Now is %d\n",__LINE__);
#else
#define debug
#endif
#ifdef ONLINE_JUDGE
char buf[1<<23],* p1=buf,* p2=buf,obuf[1<<23],* O=obuf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
using namespace std;

template<class T>inline void read(T& x)
{
	char ch=getchar();
	int fu;
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	if(ch=='-') fu=-1,ch=getchar();
	x=ch-'0';ch=getchar();
	while(isdigit(ch)) { x=x*10+ch-'0';ch=getchar(); }
	x*=fu;
}
inline int read()
{
	int x=0,fu=1;
	char ch=getchar();
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	if(ch=='-') fu=-1,ch=getchar();
	x=ch-'0';ch=getchar();
	while(isdigit(ch)) { x=x*10+ch-'0';ch=getchar(); }
	return x*fu;
}
int G[55];
template<class T>inline void write(T x)
{
	int g=0;
	if(x<0) x=-x,putchar('-');
	do { G[++g]=x%10;x/=10; } while(x);
	for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
#define N 200010
#define S 778
int n,m,q;
int a[N],b[N];
vector<int>c[N];
bool book[N];
int ans,x,s,k;
int cnt[N];
int id[N],idcnt,pos[N];
int g0[N],g1[N];
int f[N][S];
int main()
{
//	freopen("ex_light1.in","r",stdin);
//	freopen("A.out","w",stdout);
	n=read();
	m=read();
	q=read();
	for(int i=1;i<=n;i++)
	{
		b[i]=read();
	}
	//temp int cnt
	{
		int cnt=0;
		for(int i=1;i<=n;i++)
		{
			if(i==1||b[i]!=b[i-1])
			{
				a[++cnt]=b[i];
			}
		}
//		for(int i=1;i<=cnt;i++) cout<<a[i]<<" ";
		n=cnt;
	}
	a[0]=a[n+1]=m+1;
	k=s=sqrt(n);
	for(int i=0;i<=n+1;i++)
	{
		cnt[a[i]]++;
	}
	for(int i=1;i<=m+1;i++)
	{
		if(cnt[i]>k)
		{
			id[i]=++idcnt,pos[idcnt]=i;
		}
	}
	for(int i=0;i<=n+1;i++)
	{
		if(cnt[a[i]]<=k) 
		{
			if(i) g0[a[i-1]]++;
			if(i!=n+1) g0[a[i+1]]++;
			c[a[i]].push_back(i);
		}
		else
		{
			if(i) f[a[i-1]][id[a[i]]]++;
			if(i!=n+1) f[a[i+1]][id[a[i]]]++;
		}
	}
	while(q--)
	{
		int t0=0,t1=0;
		x=read();
		book[x]^=1;
		for(int i=1;i<=idcnt;i++)
		{
			if(book[pos[i]]) t1+=f[x][i];
			else t0+=f[x][i];
		}
		if(book[x]==0)
		{
			ans=ans-g0[x]-t0+g1[x]+t1;
		}
		else
		{
			ans=ans-g1[x]-t1+g0[x]+t0;
		}
		write(ans/2);
		//change
		if(cnt[x]<=k)
		{
			for(int i=0;i<c[x].size();i++)
			{
				if(book[x])
				{
					if(c[x][i]!=0) g1[a[c[x][i]-1]]++,g0[a[c[x][i]-1]]--;
					if(c[x][i]!=n+1) g1[a[c[x][i]+1]]++,g0[a[c[x][i]+1]]--;
				}
				else
				{
					if(c[x][i]!=0) g1[a[c[x][i]-1]]--,g0[a[c[x][i]-1]]++;
					if(c[x][i]!=n+1) g1[a[c[x][i]+1]]--,g0[a[c[x][i]+1]]++;
				}
			}
		}
	}
	return 0;
}

posted @ 2021-03-09 19:52  Vanilla_chan  阅读(227)  评论(0编辑  收藏  举报