杭电第2场 1004

I love counting

HDU6964

题目描述

给出\(n\)个数的序列,每个位置都有一个权值\(c\),进行 \(Q\) 次询问,每次询问给定一个区间 \([l,r]\) 和两个数字 \(a,b\),问这个序列有多少种权值 \(c\)满足 \(c ^ a <= b\)。

解题思路

莫队+分块
莫队参考 : 莫队详解 - JSOI爆零珂学家yzhang - 博客园 (cnblogs.com)
仅考虑第 \(j\)位,

\(b\)为1,\(a\)为1,那么 \(c\)为1可满足 \(c ^ a < b\)
\(b\)为1,\(a\)为1,那么 \(c\)为0可满足 \(c ^ a < b\)
\(b\)为0,\(a\)为1,那么 \(c\)只能为1才可满足 \(c ^ a <= b\)
\(b\)为0,\(a\)为0,那么 \(c\)只能为0才可满足 \(c ^ a <= b\)

所以对于前两种情况,我们直接暴力计算其对答案的贡献即可。
即:从高位往低位遍历,当第 \(j\)位出现前两种情况时,直接利用分块+前缀和 计算出当前数对答案的贡献即可。

Code

#include <bits/stdc++.h>
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define pb push_back
using namespace std;
const int N = 2e5 + 7;
const int inf = 0x3f3f3f3f;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
struct query{
	int l,r,id,bl;
	int a,b;
}q[N];
int sum[N]; //记录每个分块有多少种c
int ans[N]; 
int cnt[N]; //记录c有多少个
int c[2*N]; //c[i] 表示每个i这个数的种数
int v[N];
int k;    //记录分块大小
void add(int x)
{
	if(++cnt[x] == 1)
	{
		sum[x / k] ++;
		c[x] ++;
	}
}
void del(int x)
{
	if(--cnt[x] == 0)
	{
		sum[x / k] --;
		c[x] --;
	}
}
bool cmp(query a,query b)
{
	if(a.bl != b.bl)return a.bl < b.bl;
	else return a.r < b.r;
}
int ask(int x){ //计算前缀和,即到第x位
	int res = 0;
	for (int i = x / k * k; i <= x; i++)res += c[i];
	for (int i = 0; i < x / k; i++)res += sum[i];
	return res;
}
void solve(){
	int n,m;
	cin >> n;
	k = sqrt(n+1);
	for(int i = 1; i <= n; ++i)
		cin >> v[i];
	cin >> m;
	for(int i = 1; i <= m; ++i){
		cin >> q[i].l >> q[i].r >> q[i].a >> q[i].b;
		q[i].bl = (q[i].l - 1) / k + 1;
		q[i].id = i;
	}
	sort(q+1,q+1+m,cmp);
	int l = 1, r = 0;
	for(int i = 1; i <= m; ++i){
		int ll = q[i].l, rr = q[i].r;
		while(l < ll)
			del(v[l++]);
		while(l > ll)
			add(v[--l]);
		while(r < rr)
			add(v[++r]);
		while(r > rr)
			del(v[r--]);
		int s = 0;
		int a = q[i].a, b = q[i].b;
		for(int j = 19; j > -1; --j){
			if((b >> j) & 1){
				int p = s;
				if((a >> j) & 1)
					p |= 1 << j;
				else 
					s |= 1 << j;
				ans[q[i].id] += ask(p + (1 << j) - 1) - ask(p - 1);
			}
			else 
				if((a >> j) & 1)s |= 1 << j;
		}
		ans[q[i].id] += c[q[i].a ^ q[i].b];
	}
	for(int i = 1; i <= m; i++)
		cout << ans[i] << "\n";
}

int main()
{
	#ifdef ONLINE_JUDGE
	#else
		freopen("in.txt", "r", stdin);
		freopen("out.txt", "w", stdout);
	#endif

	qc;
	int T;
    //cin >> T;
	 T = 1;
	 while(T--){
	 	solve();
	}
	return 0;
}

上一篇:Linux笔记 — 用户和用户组的配置文件介绍


下一篇:Android平台使用Camera2(5.0+)替代过时的Camera