题目描述
题解
首先答案应该是线段树,但是对于每次更改非连续的几段难以用线段树,所以把答案转换为:亮着的灯数量-相邻两灯都亮的对数,前者预处理,后者可以用一个分块。
把灯数量超过的颜色称为大色,其余称为小色,那么对于每种颜色,记为与第 i 种颜色的灯相邻的、亮着的、颜色为小色的灯的数量,那么维护它只需要每次修改小色对所有相邻颜色做贡献(),然后对于当前修改的颜色 i,枚举每一种亮着的大色 j (),算上 i 与 j 相邻的灯的对数(对于 i 是小色,直接枚举相邻颜色,对于 i 是大色,可以预处理与其他大色相邻的灯的对数)的贡献。当时,总复杂度。
考场上为了节约时间,直接码数据结构,还做了一些后来发现很蠢的多余操作,然后因常数大而T。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#define ll long long
#define uns unsigned
#define MAXN 100005
#define INF 0x3f3f3f3f
using namespace std;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
return f?x:-x;
}
int n,m,q,c[MAXN],N,num[MAXN];
int id[MAXN],d[320],IN,ans;
int ln[320][320],nl[MAXN];
vector<int>p[MAXN];
bool vis[MAXN];
signed main()
{
// freopen("light.in","r",stdin);
// freopen("light.out","w",stdout);
n=read(),m=read(),q=read(),N=310;
for(int i=1;i<=n;i++)c[i]=read(),num[c[i]]++;
for(int i=1;i<=m;i++)if(num[i]>N)id[i]=++IN,d[IN]=i;
for(int i=2;i<=n;i++){
if(c[i]==c[i-1])num[c[i]]--;
else{
p[c[i]].push_back(i-1),p[c[i-1]].push_back(i);
if(id[c[i]]>0&&id[c[i-1]]>0)ln[id[c[i-1]]][id[c[i]]]++;
}
}
while(q--){
int a=read();
if(vis[a])ans-=num[a],ans+=nl[a];
else ans+=num[a],ans-=nl[a];
if(id[a]>0){
for(int i=1;i<=IN;i++)
if(vis[d[i]]){
if(vis[a])ans+=ln[i][id[a]]+ln[id[a]][i];
else ans-=ln[i][id[a]]+ln[id[a]][i];
}
}else{
for(uns i=0;i<p[a].size();i++)
if(id[c[p[a][i]]]>0&&vis[c[p[a][i]]]){
if(vis[a])ans++;
else ans--;
}
}
if(id[a]==0){
for(uns i=0;i<p[a].size();i++){
if(vis[a])nl[c[p[a][i]]]--;
else nl[c[p[a][i]]]++;
}
}
vis[a]^=1;
printf("%d\n",ans);
}
return 0;
}