题意:给你n个数和一个k,现在q次询问,每次给你两个区间L~R, U~V, 问你从L~R中取一个x,从U~V中取一个y,使
得x+y = k的方案数。 N(1≤N≤30000) M(1≤M≤30000) Li,Ri,Ui,Vi(1≤Li≤Ri
思路:这题的每次询问有两个区间,不好操作,因为这题的方案数区间与区间之间具有可加性,也就是说询问L~R和
U~V,我们可以求出四个区间,再通过容斥求的需要的,可以变成(L~V)- (L~U-1)- (R+1~ V) + (R+1 ~ U-1),给
这四个区间加上个标记是加还是减就行了。
代码:
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 1e6+5;
int a[maxn], ans[maxn], num[maxn], n, m, k, cnt, unit, tmp;
struct node
{
int id, l, r, sta, blk;
bool operator < (const node &a) const
{
if(blk == a.blk) return r < a.r;
else return blk < a.blk;
}
}op[maxn];
void add(int x)
{
if(k-x >= 0) tmp += num[k-x];
num[x]++;
}
void del(int x)
{
if(k-x >= 0) tmp -= num[k-x];
num[x]--;
}
void solve()
{
memset(ans, 0, sizeof(ans));
memset(num, 0, sizeof(num));
int l = 1, r = 0;
tmp = 0;
for(int i = 0; i < cnt; i++)
{
while(r < op[i].r) add(a[++r]);
while(r > op[i].r) del(a[r--]);
while(l < op[i].l) del(a[l++]);
while(l > op[i].l) add(a[--l]);
ans[op[i].id] += (op[i].sta) ? tmp : -tmp;
}
for(int i = 1; i <= m; i++)
printf("%d\n", ans[i]);
}
int main(void)
{
while(cin >> n >> k)
{
unit = sqrt(n);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
scanf("%d", &m);
cnt = 0;
for(int i = 1; i <= m; i++)
{
int l, r, u, v;
scanf("%d%d%d%d", &l, &r, &u, &v);
op[cnt++] = node{i, l, v, 1, l/unit};
op[cnt++] = node{i, l, u-1, 0, l/unit};
op[cnt++] = node{i, r+1, v, 0, (r+1)/unit};
op[cnt++] = node{i, r+1, u-1, 1, (r+1)/unit};
}
sort(op, op+cnt);
solve();
}
return 0;
}