Luogu:P3870 [TJOI2009]开关 + P2709 小B的询问(今日分块刷题小记)

P3870 [TJOI2009]开关

题目

P3870 [TJOI2009]开关
在这里插入图片描述

算法分析

用分块来解决的话思路很简单,就是把整个序列进行分块,区间 [ a , b ] [a,b] [a,b]按照三个部分(前小块,中部整块,后小块)大段维护,局部朴素的算法来搞就行了。

具体代码如下:( f [ i ] f[i] f[i]表示灯的状态)
注意本题中灯开 o r or or关的状态改变的时候可用 f [ i ] f[i] f[i] ^ = 1 =1 =1来实现,否则会TLE。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define rg register
using namespace std;
typedef long long ll;
inline int sread()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
	return f*x; 
} 
const int maxn=100000;
int n,m,a,b,klen,ans,opt;
int knum[maxn],f[maxn],fchange[maxn];
void change_(int a,int b)
{
	if(knum[a]==knum[b])
	{
		for(rg int i=a;i<=b;++i)	f[i]^=1;
		return;	
	}
	for(rg int i=a;knum[i]==knum[a];i++)	f[i]^=1;
	for(rg int i=b;knum[i]==knum[b];i--)	f[i]^=1;
	for(rg int i=knum[a]+1;i<=knum[b]-1;++i)	fchange[i]^=1;
	return;
}
void search_(int a,int b)
{
	for(rg int i=a;i<=b;++i)
		ans+=(f[i]+fchange[knum[i]])%2;
	printf("%d\n",ans);
}
int main()
{
	n=sread();	m=sread();
	klen=sqrt(n);
	for(rg int i=1;i<=n;++i) knum[i]=(i-1)/klen+1;
	for(rg int i=1;i<=m;++i)
	{
		opt=sread(); a=sread();	b=sread();
		if(opt==0) 	change_(a,b);
		else if(opt==1) search_(a,b);
		ans=0;
	}
	return 0;
}

反思与总结

因为前几次提交都是TLE,很蓝瘦。
最开始是这样搞

f[i] = (f[i] + 1) % 2;

20分TLE emm后来这样搞

f[i] ^= 1;

60分TLE emm接着发现了这个

if( ( f[i] + fchange[ knum[i] ]) % 2 ) ans++;

搞成这样

ans += ( f[i] + fchange[ knum[i] ] ) % 2;

OK Accepted

当测试状态从一片蓝变成一片绿的时候心情十分舒畅。

P2709 小B的询问【普通莫队模板】

莫队相关知识及本题思路分析详见 这篇文章

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define rg register
using namespace std;
typedef long long ll;
inline int sread()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
	return f*x; 
} 
const int maxn=50010;
int n,m,k,ql,qr,klen,ans;
int a[maxn];
int sum[maxn];
int ans_[maxn];
struct node{
	int l;
	int r;
	int num;
}q[maxn];
bool cmp1(node a,node b)
{
	return ((a.r/klen)==(b.r/klen))?a.l<b.l:a.r<b.r;
}
inline void add(int x)
{
	sum[a[x]]++;
	ans+=2*sum[a[x]]-1; 
}
inline void del(int x)
{
	sum[a[x]]--;
	ans-=2*sum[a[x]]+1; 
}
int main()
{
	n=sread();	m=sread();	k=sread();
	klen=sqrt(n);
	for(rg int i=1;i<=n;++i)
		a[i]=sread();
	for(rg int i=1;i<=m;++i)
	{
		q[i].l=sread();	q[i].r=sread();
		q[i].num=i;
	}
	sort(q+1,q+m+1,cmp1);
	int l=1,r=0;
	for(rg int i=1;i<=m;++i)
	{
		while(l<q[i].l) { del(l); l++; }
		while(l>q[i].l) { l--; add(l); }
		while(r>q[i].r) { del(r); r--; }
		while(r<q[i].r) { r++; add(r); }
		ans_[q[i].num]=ans;
	}
	for(rg int i=1;i<=m;++i)
	{
		printf("%d\n",ans_[i]);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值