CF EDU 41 E 题 Tufurama 【思维 + 树状数组】

传送门
题意: 给定n个数, 你需要找到有多少个下标二元组(x, y), 满足x < y, a[x] >= y, a[y] >= x.

思路: 这道题挺有意思的, 首先我们要维护好a[y] >= x, 这个值, 即我们对x这个下标存储最大的id(id < y), 使得a[id] >= x, 这个我们就预处理好了第二个条件, 那么我们如何计数第二个条件了, 那就要用到树状数组了, 我们还要确定的一个事情就是a[i] >= n时, 就可以将a[i] = n来处理了, 这样是不会影响答案的, 也方便运用树状数组.. 我们从开头开始插入a[i], 然后对于它的最大那些id们算一次答案, 直接就是getsum(n) - getsum(id-1), 所以一直处理完全部的数, 这样累加就可以得到ans. 考虑下正确性, 假设1 - 5, 2 - 4, a[1] = 5, a[5] = 1, a[2] = 4, a[4] = 3, 那么当处理到2时, 累加的值应该是2, 而对应的二元组是(1, 4), (2, 4), 因为1一定满足了a[1] >=4, 又因为a[4] 是一定 >= 1, 如果a[4] < 1, 他就会在i-1的后面, 而不是i后面…….

AC Code

const int maxn = 2e5+5;
int c[maxn], n;
void add(int x) {
    for (; x <= n ; x += x & -x)
        c[x]++;
}
int getsum(int x) {
    int res = 1;
    for ( ; x ; x -= x & -x) {
        res += c[x];
    }
    return res;
}
vector<int>ve[maxn];
int a[maxn];
void solve()
{
    while(~scanf("%d", &n)) {
        Fill(c, 0);
        for (int i = 1 ; i <= n ; i ++) {
            scanf("%d", a + i);
            a[i] = min(a[i], n);
            ve[min(i-1, a[i])].pb(i);
        }
        ll ans = 0;
        for (int i = 1 ; i <= n ; i ++) {
            add(a[i]);
            for (int j = 0 ; j < sz(ve[i]) ; j ++) {
                int p = ve[i][j];
                ans += getsum(n) - getsum(p-1);
            }
        }
        printf("%lld\n", ans);
        for (int i = 1 ; i <= n ; i ++) ve[i].clear();
    }
}

你可能感兴趣的:(树状数组,想法思维题)