莫队算法的模板题,洛谷上面的数据太强,主席树过了,莫队被卡掉了,BZOJ上面可以用莫队过。
莫队的思想就是加速了暴力的过程,分成了sqrt N
个块。排序的时候如果在同一个块里面就按照r
从小到大排序,不在同一个块里面就按照l
从小到大排序
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N],cnt[N];
int tim,ans[N];
int curl=1,curr=0,res=0;
inline int read()
{
char ch=getchar();
int x=0;
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}
inline void add(int pos)
{
cnt[a[pos]]++;
if(cnt[a[pos]]==1)
res++;
}
inline void remove(int pos)
{
cnt[a[pos]]--;
if(cnt[a[pos]]==0)
res--;
}
struct query
{
int l,r,id;
} e[N];
bool cmp(query x,query y)
{
if(x.l/tim==y.l/tim)//在同一个块
return x.r<y.r;
else
return x.l<y.l;
}
int main()
{
int n,m;
n=read();
for(int i=1; i<=n; i++)
a[i]=read();
m=read();
tim=sqrt(m);
for(int i=1; i<=m; i++)
{
e[i].l=read();
e[i].r=read();
e[i].id=i;
}
sort(e+1,e+1+m,cmp);
for(int i=1; i<=m; i++)
{
int l=e[i].l,r=e[i].r;
while(curl<l) remove(curl++);
while(curl>l) add(--curl);
while(curr<r) add(++curr);
while(curr>r) remove(curr--);
ans[e[i].id]=res;
}
for(int i=1; i<=m; i++)
printf("%d\n",ans[i]);
return 0;
}
/*
10
1 2 3 1 1 2 1 2 3 1
1
1 2
*/
CF 340 E. XOR and Favorite Number
//CF#340 DIV2 E MO's Algorithm
//这题思路是首先求出前缀和,所以就会有sum[L-1]^sum[R]==k的公式,但是左边是两个都是不清楚值的,所以要转变一下,
//变为sum[R]^k==sum[L-1],这样子我只要求出现在存在多少个sum[L-1]就知道有多少个可以和sum[k]异或为k的了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1<<20;
typedef long long LL;
LL Ans[maxn], ans;
int sum[maxn], num[maxn], pos[maxn], k, block;
int n, m;
struct Q{
int l, r, id;
}q[maxn];
bool cmp(Q a, Q b){
if(pos[a.l] == pos[b.l]) return a.r < b.r;
return pos[a.l] < pos[b.l];
}
void add(int x){
/*
当前sum[L-1]^sum[R]=k,所以只要知道当前sum[L-1]有多少个
即有多少个是在[L-1,R]区间异或是等于k的
*/
ans += num[sum[x]^k];
num[sum[x]]++; //增加当前前缀和个数
//这里后来才加是为了防止增加为本身
}
void del(int x){
num[sum[x]]--;//一个数有可能异或后还是本身,所以要先减去本身
ans -= num[sum[x]^k];
}
int main(){
scanf("%d%d%d", &n, &m, &k);
block = sqrt(n);
memset(num, 0, sizeof(num));
sum[0] = 0;
for(int i = 1; i <= n; i++){
scanf("%d", &sum[i]);
sum[i] ^= sum[i-1];
pos[i] = (i - 1)/ block;
}
for(int i = 1; i <= m; i++){
scanf("%d%d", &q[i].l, &q[i].r);
q[i].id = i;
}
sort(q + 1, q + m + 1, cmp);
int L = 1, R = 0;
ans = 0;
num[0] = 1;
for(int i = 1; i <= m; i++){
int id = q[i].id;
while(R < q[i].r){
R++;
add(R);
}
while(L > q[i].l){
L--;
add(L-1);
}
while(R > q[i].r)
{
del(R);
R--;
}
while(L < q[i].l)
{
del(L-1);
L++;
}
Ans[id] = ans;
}
for(int i = 1; i <= m; i++){
printf("%lld\n", Ans[i]);
}
return 0;
}
题意:给你一个序列a,长度为n,有m次操作,每次询问一个区间是否可以选出两个数它们的差为x,或者询问一个区间是 否可以选出两个数它们的和为x,或者询问一个区间是否可以选出两个数它们的乘积为x ,这三个操作分别为操作1 ,2,3选出的这两个数可以是同一个位置的数。
解法:考虑一下要对区间信息查询,我们询问离线,跑个莫队,然后用bitset记录下哪些数出现了。然后区间a - b = x -> a = b + x , 同理 a + b = x -> a = x - b -> a = - (b - x),所以对于差为x我们直接把第一个bitset右移然后和自己取交,同理对于和为x的情况,我们对之前的那个bitset取反,进行类似的操作就可以了,然后对于乘法的话 a*b = x我们直接枚举质因子暴力判断即可。
//BZOJ 4810
#include <bits/stdc++.h>
using namespace std;
//a - b = x -> a = b + x
//a + b = x -> a = -b + x -> a = - (b - x)
//a * b = x -> a = x / b
//O(n*n/32)
const int maxn = 100010;
const int offset = 100000;
bitset <maxn> pos;
bitset <maxn> neg;
int n, m, belong[maxn], block, a[maxn], ans[maxn], cnt[maxn];
struct node{
int l, r, x, id, op;
node(){}
node(int l, int r, int x, int id, int op):l(l),r(r),x(x),id(id),op(op){}
bool operator < (const node &rhs) const{
if(belong[l] != belong[rhs.l]){
return belong[l] < belong[rhs.l];
}
else{
return r < rhs.r;
}
}
}q[maxn];
void input()
{
scanf("%d%d", &n, &m);
block = ceil(sqrt(n));
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++){
belong[i] = (i-1)/block+1;
}
for(int i = 1; i <= m; i++){
scanf("%d%d%d%d", &q[i].op, &q[i].l, &q[i].r, &q[i].x);
q[i].id = i;
}
sort(q + 1, q + m + 1);
}
void work()
{
int L = 1, R = 0;
for(int i = 1; i <= m; i++)
{
int id = q[i].id;
while(R < q[i].r){
R++;
cnt[a[R]]++;
pos[a[R]] = 1;
neg[offset - a[R]] = 1;
}
while(L > q[i].l){
L--;
cnt[a[L]]++;
pos[a[L]] = 1;
neg[offset - a[L]] = 1;
}
while(R > q[i].r){
cnt[a[R]]--;
if(cnt[a[R]] == 0) pos[a[R]] = 0;
if(cnt[a[R]] == 0) neg[offset - a[R]] = 0;
R--;
}
while(L < q[i].l){
cnt[a[L]]--;
if(cnt[a[L]] == 0) pos[a[L]] = 0;
if(cnt[a[L]] == 0) neg[offset - a[L]] = 0;
L++;
}
if(q[i].op == 1) //+
{
if(((pos>>q[i].x)&pos).any()){
ans[id] = 1;
}
else{
ans[id] = 0;
}
}
else if(q[i].op == 2){//-
if(((neg>>(offset-q[i].x))&pos).any()){
ans[id] = 1;
}
else{
ans[id] = 0;
}
}
else{
if(q[i].x == 0 && pos[0])
{
ans[id] = 1;
continue;
}
for(int j = 1; j*j <= q[i].x; j++)
{
if(q[i].x % j == 0 && pos[j] == 1&& pos[q[i].x/j] == 1){
ans[id] = 1;
break;
}
}
}
}
}
void output()
{
for(int i = 1; i <= m; i++){
puts(ans[i]?"yuno":"yumi");
}
}
int main()
{
input();
work();
output();
}