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
这道题的思考过程分为两个部分。
- 暴力处理。
- 对于要修改的颜色的灯的数量进行根号分治。
暴力:只有当要修改的灯的两侧的灯的亮灭情况相同时,才会对答案造成影响(\(\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;
}