2018牛客网多校第一场 Different Integers

题意:

1.给你一个n个数的数列和q次询问l, r,需要回答a[1]…a[l], a[r]…a[n]的不同数的个数。
n,q <=1e5,a[i] <= n
固定块大小的莫队会TLE

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9+7;
const int maxn = 1e6 + 10;
const int inf = 0x3f3f3f3f;
#define met(a, b) memset(a, b, sizeof(a))
struct MATION {
    int l, r, id;
} q[2 * maxn];
ll cnt = 0;
int pos[maxn], block, val[maxn], ANS[maxn], a[maxn];
bool vis[maxn];
int d, n, m, top = 0;
int cmp(MATION a, MATION b) {
    if(pos[a.l] != pos[b.l]) return pos[a.l] < pos[b.l];
    else return a.r < b.r;
}
void solve() {
    met(val, 0);
    int ans = 0;
    int l = q[0].l, r = q[0].l - 1;
    for(int i = 0; i < top; i++) {
        while(l < q[i].l) {
            val[a[l]]--;
            if(val[a[l]] == 0) ans--;
            l++;
        }
        while(l > q[i].l) {
            l--;
            val[a[l]]++;
            if(val[a[l]] == 1) ans++;
        }
        while(r < q[i].r) {
            r++;
            val[a[r]]++;
            if(val[a[r]] == 1) ans++;
        }
        while(r > q[i].r) {
            val[a[r]]--;
            if(val[a[r]] == 0) ans--;
            r--;
        }
        ANS[q[i].id] = ans;
    }
}
int main() {
    while(~scanf("%d%d", &n, &m)) {
        met(vis, false);
        cnt = 0;
        block = sqrt(n);
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            pos[i] = (i - 1) / block + 1;
            pos[i + n] = (i + n - 1) / block + 1;
            a[i + n] = a[i];
            if(!vis[a[i]]) {
                vis[a[i]] = true;
                cnt++;
            }
        }
        int l, r;
        top = 0;
        for(int i = 0; i < m; i++) {
            scanf("%d%d", &l, &r);
            if(r <= l) {
                ANS[i] = cnt;
                continue;
            }
            q[top].l = r;
            q[top].r = n + l;
            q[top].id = i;
            top++;
        }
        sort(q, q + top, cmp);

        solve();
        for(int i = 0; i < m; i++) {
            //cout << ANS[i * 2] << " * " << ANS[i * 2 + 1] << endl;
            printf("%d\n",ANS[i]);
        }
    }
    return 0;
}   
  1. 普通树状数组做法
    原做法基本操作讲解 http://www.cnblogs.com/chenquanwei/articles/9340577.html
    将数组复制一遍即可 https://blog.csdn.net/Coldfresh/article/details/81122267

(标程做法)
加一个first数组需要转化:https://blog.csdn.net/c6376315qqso/article/details/81130241
转化思路 :转化为求n- (a[1]-a[l] +a[r]-a[n])没出现过得数的个数
即在a[l+1]-a[r]出现过 而在其他区间没出现过得数的个数
即加一个first数组
之前是add(last[a[i]]]) 求的是在本区间出现过得数的个数但是本区间出现过得数其实是有可能在其他区间再次出现的,而加了first数组之后,因为for循环,断绝了a[r]右边的区间,又因为first断绝了这些数在a[l]左边出现的情况。(如果在a[l]左边出现,则不会被统计到a[l],a[r]区间内)

拓展题:https://blog.csdn.net/alan_cty/article/details/52810570
给出n个数,m次询问,每次询问区间[l,r]中出现次数为偶数的数的异或和。
我们通过求出区间内不同的数(每个数只有一个)的异或和^(区间内数的整体异或和即区间内出现奇数次数的异或和即可求出答案),而关键在于求出区间内不同的数(每个数只有一个)的异或和,这里我们就得再次用到上文中树状数组那个方法里。只不过这里树状数组要更换成异或版的。

#include<bits/stdc++.h>
#define fir first
#define sec second
#define pii pair<int,int>
#define debug(x) cout<<#x<<" = "<<x<<endl;
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 1e5 + 5;

int fir[MAXN], last[MAXN];
int tree[MAXN];
int n, q;

struct Query {
    int l, r;
    bool operator < (const Query & x) const {
        return r < x.r;
    }
    int id;
} que[MAXN];


void add(int i, int v) {
    while(i <= n) {
        tree[i] += v;
        i += i&-i;
    }
}

int query(int i) {
    int ret = 0;
    while(i) {
        ret += tree[i];
        i -= i&-i;
    }
    return ret;
}

int ans[MAXN];
int a[MAXN];


int main() {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
#endif
    while(~scanf("%d %d", &n, &q)) {
        memset(fir, 0, sizeof(fir));
        memset(tree, 0, sizeof(tree));
        int tot = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            if(!fir[a[i]]) fir[a[i]] = i, tot++;
            last[a[i]] = i;
        }
        for(int i = 1; i <= q; i++) scanf("%d %d", &que[i].l, &que[i].r), que[i].id = i;
        sort(que + 1, que + 1 + q);
        int p = 1;
        for(int i = 1; i <= n; i++) {
            while(p <= q && que[p].r == i) {
                int id = que[p].id;
                ans[id] = tot - query(i) + query(que[p].l);
                p++;
            }
            if(last[a[i]] == i) add(fir[a[i]], 1);
        }
        for(int i = 1; i <= q; i++) printf("%d\n", ans[i]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值