题意:
Lucky
Accepts: 34
Submissions: 267
Time Limit: 6000/3000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
问题描述
wld有n个数(a1...an) 保证对于任意 1≤i≤n,1≤ai≤n wld有一个常数k保证 2≤k≤2∗n 为了消除歧义保证k为奇数 他有 m 个询问 每个询问有参数 l1,r1,l2,r2 保证 (1≤l1≤r1<l2≤r2≤n) 对于每个询问你需要回答有多少个二元组 (i,j) 满足: l1≤i≤r1 且 l2≤j≤r2 且 ai+aj=k 保证 1≤n≤30000,1≤m≤30000
输入描述
多组数据(最多 5 组) 对于每组数据: 第一行:一个数 n 表示数的个数 接下来一行:一个数 k 表示wld的常数 接下来一行: n 个数,依次为 a1,a2,…an 接下来一行:一个数 m 表示询问数 接下来 m 行:四个数 l1,r1,l2,r2 表示这组询问的参数
输出描述
对于每组数据: 对于每个询问输出二元组的数目
输入样例
5 3 1 2 1 2 3 1 1 2 3 5
输出样例
2
Hint
a1 + a4 = 3 a2 + a3 = 3
1.一种神奇的离线分块法,貌似大家都叫它莫队算法
2.可以以nsqrt(n)的均摊复杂度处理区间查询(假设这个区间询问可以用区间长度的复杂度计算出来的话,且如果我已
经知道了A区间询问的信息,再询问包含A区间的B区间询问时,我可以不再重新计算A区间了,直接计算剩下的部分
就可以了)
3.方法是先将区间划分为sqrt(n)个sqrt(n)大小的区间
4.然后预处理:排序,先按左端点l所在的块号排序,块号相同的时候再按r的大小排序
5.这个图是对于区间询问的最坏情况
6.对于第一次询问,是枚举‘二’端点左边sqrt(n)的复杂度,再枚举‘二’端点右边sqrt(n)的复杂度
对于第二次询问,是枚举‘二’端点左边sqrt(n)的复杂度,再枚举‘二’端点右边一直到’三‘端点右边sqrt(n)的复杂度(因为
可以用到开始询问的区间1的右半部分)
7.这种最坏的询问方式,产生的复杂度是:比如有n次询问吧,每次询问都需要枚举某个端点左边sqrt(n),而对于某个
端点右边,也就是最多枚举整个大区间sqrt(n)次
8.最坏均摊复杂度就是nsqrt(n)
9.对于这个题目还需要用一下容斥原理,改成4个区间查询
总结:
1.舍友yyz大神还教我一种在线版的分块大法,回来试一试
2.觉着莫队算法很棒,暂时还是不能总结出个所以然来,不过随着知识体系的建立,应该会有一些新的感悟吧
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
#define MAXN 30005
#define MAXM 200
struct Node
{
int l,r,id,add;
bool operator < (const Node & c)const{
return l / MAXM == c.l / MAXM ? r < c.r : l / MAXM < c.l / MAXM;
}
}q[MAXN << 2];
int k,n,m,num[MAXN],has[MAXN],last[MAXN];
void solve()
{
int mid,r,cur = -1;
int ans1,ans2;
for(int j = 0;j < m << 2;j++)
{
int ind = q[j].l / MAXM;
if(ind != cur)
{
memset(has,0,sizeof(has));
cur = mid;
mid = (ind + 1) * MAXM , r = (ind + 1) * MAXM;
ans2 = 0;
}
ans1 = 0;
for(int i = r;i < q[j].r;i++)
{
if(k - num[i] <= n && 0 <= k - num[i])
ans2 += has[k - num[i]];
has[num[i]]++;
}
for(int i = q[j].l;i < min(q[j].r,mid);i++)
{
if(k - num[i] <= n && 0 <= k - num[i])
ans1 += has[k - num[i]];
has[num[i]]++;
}
for(int i = q[j].l;i < min(q[j].r,mid);i++)
has[num[i]]--;
//cout << ans << " ans " << endl;
r = max(q[j].r,r);
last[q[j].id] += q[j].add * (ans1 + ans2);
cur = ind;
}
}
int main()
{
int l1,l2,r1,r2;
while(scanf("%d%d",&n,&k) != EOF)
{
memset(last,0,sizeof(last));
for(int i = 0;i < n;i++)
scanf("%d",&num[i]);
scanf("%d",&m);
for(int i = 0;i < m;i++)
{
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
l1--,l2--;
q[i * 4].l = l1,q[i * 4].r = r2,q[i * 4].id = i,q[i * 4].add = 1;
q[i * 4 + 1].l = l1,q[i * 4 + 1].r = l2,q[i * 4 + 1].id = i,q[i * 4 + 1].add = -1;
q[i * 4 + 2].l = r1,q[i * 4 + 2].r = r2,q[i * 4 + 2].id = i,q[i * 4 + 2].add = -1;
q[i * 4 + 3].l = r1,q[i * 4 + 3].r = l2,q[i * 4 + 3].id = i,q[i * 4 + 3].add = 1;
}
sort(q,q + m * 4);
solve();
for(int i = 0;i < m;i++)
printf("%d\n",last[i]);
}
}