HDU 5213 Lucky(莫队+容斥)

题意:给你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;
}


你可能感兴趣的:(莫队算法,分块)