HDU 5213 Lucky(莫队+容斥)

来源:互联网 发布:恒大淘宝队 编辑:程序博客网 时间:2024/06/06 01:03

题意:给你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<Ui≤Vi≤N)


思路:这题的每次询问有两个区间,不好操作,因为这题的方案数区间与区间之间具有可加性,也就是说询问L~R和

U~V,我们可以求出四个区间,再通过容斥求的需要的,可以变成(L~V)- (L~U-1)- (R+1~ V) + (R+1 ~ U-1),给

四个区间加上个标记是加还是减就行了。


代码:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>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;}


原创粉丝点击