【WOJ 2443 L的鞋子】【luoguP4135 作诗】(分块猛草)

不会莫队+在线只能用分块

卡常卡了一个小时 草 整个人都不好了

自己YY出来的 记录a[i][j]表示i在块j中出现的次数。

对于这个东西求出前缀和ss方便后面使用。

再记录sum[i][j]表示块[i,j]中满足条件的个数,这玩意可以预处理出来。

然后对于询问(x,y),枚举左右多出来的那一截里面的数带来的贡献(有可能为负),加上中间的sum即可。

注意特判,以及对于50000以上的数据直接分sqrt个是会MLE的,要特殊处理。

然后卡常的话:register+dzyoの快读+手动memset才过 MMP

#include<bits/stdc++.h>
#define re register 
const int N=100005;
const int K=220;
using namespace std;
const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline int read() {
	char ch=nc(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
	return i*f;
}
inline void write(int x)
{
	if(x<0)
	{
		x=-x;
		putchar('-');
	}
	if(x>9) write(x/10);
	putchar((x%10)+'0');
}
int n,c,m,num,k,a[N][K+5],v[N],b[N],ss[N][K+5],sum[K+5][K+5];	//sum:第i个块到第j个块中有多少合法答案 
int l[K],r[K],temp[N],op[N];
bool vis[N];
inline void Solve(int st)	//从第l个块往后处理sum
{
	memset(temp,0,sizeof(temp));
	int k=0;
	for(re int ed=st;ed<=num;ed++)
	{
		for(re int i=l[ed];i<=r[ed];i++)
		{
			if(temp[v[i]]&1) k++;
			else if(temp[v[i]]) k--;
			temp[v[i]]++;
		}
		sum[st][ed]=k;
	}
} 
vector <int> vec;
inline int query(int x,int y)
{
	if(b[x]==b[y])
	{
		int ans=0;
		for(int i=x;i<=y;i++) vis[v[i]]=0,op[v[i]]=0;
		for(int i=x;i<=y;i++) op[v[i]]++;
		for(int i=x;i<=y;i++)
		{
			if(vis[v[i]]) continue;
			vis[v[i]]=1;
			if(!(op[v[i]]&1)) ans++;
		}
		return ans;
	}
	else
	{
		vec.clear();
		int ans=0;
		int st=b[x]+1,ed=b[y]-1;
		for(re int i=x;i<=min(b[x]*k,y);i++) op[v[i]]=0,vis[v[i]]=0;
		for(re int i=(b[y]-1)*k+1;i<=y;i++) op[v[i]]=0,vis[v[i]]=0;
		for(re int i=x;i<=min(b[x]*k,y);i++) vec.push_back(v[i]),op[v[i]]++;
		for(re int i=(b[y]-1)*k+1;i<=y;i++) vec.push_back(v[i]),op[v[i]]++;
		for(re int i=0;i<vec.size();i++)
		{
			if(vis[vec[i]]) continue;
			vis[vec[i]]=1;
			int cnt=op[vec[i]];
			int tmp=ss[vec[i]][ed]-ss[vec[i]][st-1];
			if(!tmp)
			{
				if(!(cnt&1)) ans++;
				continue;
			}
			if(tmp&1&&cnt&1) ans++;
			if(!(tmp&1)&&cnt&1) ans--;
		}
		return ans+sum[st][ed];
	}
}
int main()
{
	n=read(),c=read(),m=read();
	k=sqrt(n);	num=n/k;
	if(n>=50000) num=K,k=n/num;
	if(n%k!=0) num++;
	
	for(re int i=1;i<=n;i++)
	{
		v[i]=read();
		b[i]=(i-1)/k+1;
		a[v[i]][b[i]]++;
	}
	for(re int i=1;i<=c;i++)
		for(re int j=1;j<=num;j++)
			ss[i][j]=ss[i][j-1]+a[i][j];
	for(re int i=1;i<=num;i++) l[i]=(i-1)*k+1,r[i]=l[i]+k-1;
	r[num]=n;
	for(re int i=1;i<=num;i++) Solve(i);
	int x,y;
	for(re int i=1;i<=m;i++)
	{
		x=read(),y=read();
		write(query(x,y)),putchar('\n');
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值