hdu 5213

问题描述

wldn个数(a1...an)

保证对于任意$1 \leq i \leq n1 \leq ai \leq n$

wld有一个常数k保证2 \leq k\leq 2*n2≤k≤2n

为了消除歧义保证k为奇数

他有mm个询问

每个询问有参数l1,r1,l2,r2l1,r1,l2,r2

保证(1 \leq l1\leq r1 < l2 \leq r2 \leq n)(1≤l1≤r1<l2≤r2≤n)

对于每个询问你需要回答有多少个二元组$(ij)$满足:

l1 \leq i\leq r1l1≤ir1l2 \leq j \leq r2l2≤jr2ai + aj = kai+aj=k

保证1 \leq n\leq 30000, 1 \leq m \leq 300001≤n≤30000,1≤m≤30000

输入描述

多组数据(最多55)

对于每组数据:

第一行:一个数nn表示数的个数

接下来一行:一个数kk表示wld的常数

接下来一行:nn个数,依次为$a1, a2, … an$

接下来一行:一个数mm表示询问数

接下来mm行:四个数l1, r1, l2, r2l1,r1,l2,r2表示这组询问的参数

输出描述

对于每组数据:

对于每个询问输出二元组的数目

输入样例

5

3

1 2 1 2 3

1

1 2 3 5

输出样例

2

Hint

a1 + a4 = 3

a2 + a3 = 3

思路:序列莫队加上容斥定理

设f(x,y)为区间[x,y]满足a[x]+a[y]=k的个数,则f(l1,r1,l2,r2)=f(l1,r2)-f(r1+1,r2)-f(l1,l2-1)+f(r1+1,l2-1);

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 30005;
const int blocksize = 200;
struct node
{
	int left, right, id, p;
	void init(int l1, int r1, int id1, int p1)
	{
		left = l1, right = r1;
		id = id1, p = p1;
	}
}query[4 * maxn];
bool cmp(node &a, node &b)
{
	if (a.left / blocksize != b.left / blocksize) return a.left / blocksize < b.left / blocksize;
	return  a.right < b.right;
}
int cnt[2 * maxn], a[maxn], ans[maxn], n, k;
int main()
{
	while (scanf("%d", &n) != EOF)
	{
		int cnt1 = 0;
		scanf("%d", &k);
		for (int i = 1;i <= n;i++)
			scanf("%d", &a[i]);
		memset(cnt, 0, sizeof(cnt));
		memset(ans, 0, sizeof(ans));
		int q;
		scanf("%d", &q);
		for (int i = 1;i <= q;i++)
		{
			int l1, r1, l2, r2;
			scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
			query[cnt1++].init(l1, r2, i, 1);
			query[cnt1++].init(r1 + 1, r2, i, -1);
			query[cnt1++].init(l1, l2 - 1, i, -1);
			if (r1 + 1 <= l2 - 1) query[cnt1++].init(r1 + 1, l2 - 1, i, 1);
		}
		sort(query, query + cnt1, cmp);
		int left = 1, right = 0, res = 0;
		for (int i = 0;i < cnt1;i++)
		{
			while (left > query[i].left)
			{
				left--;
				if (k > a[left] && k - a[left] <= n)
					res += cnt[k - a[left]];
				cnt[a[left]]++;
			}
			while (right < query[i].right)
			{
				right++;
				if (k > a[right] && k - a[right] <= n)
					res += cnt[k - a[right]];
				cnt[a[right]]++;
			}
			while (left < query[i].left)
			{
				cnt[a[left]]--;
				if (k > a[left] && k - a[left] <= n)
					res -= cnt[k - a[left]];
				left++;
			}
			while (right > query[i].right)
			{
				cnt[a[right]]--;
				if (k > a[right] && k - a[right] <= n)
					res -= cnt[k - a[right]];
				right--;
			}
			ans[query[i].id] += res*query[i].p;
		}
		for (int i = 1;i <= q;i++)
			printf("%d\n", ans[i]);
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值