“玲珑杯”线上赛 Round #24 E 题 【思维 + 树状数组】

传送门
// 题意: 给定一个长度为n的序列, 问它的2^n个子序列的逆序对之和是多少.

这道题和牛客上面一道题很相似, 牛客上面是求它的n(n+1)/2个子区间的逆序对之和.

明显这个问题依旧考虑贡献问题, 所以想一想就知道每一个逆序对的贡献就是2^(n-2)次方, 因为去除掉逆序对的那两个数, 剩下的数的任意一个子序列再加上这两个数就行啦. 所以ans 就是逆序对数 * 2^(n - 2)次方.

(原先没仔细想之前以为会更难, 想了后才发现这么简单, 比那个区间问题简单多了~)

AC Code

const int maxn = 1e6+5;
int c[maxn];        //树状数组
vector<int >ve;
int a[maxn];        //用来存原始状态
int n,k;
void add(int x) { for(;x<=n;x+=x&-x) c[x]++; }
int getsum(int x) { int res=0; for(;x;x-=x&-x) res += c[x]; return res; }
int getid(int x)
{
    return lower_bound(ve.begin(),ve.end(),x)-ve.begin() + 1;
}
ll qpow(ll x, ll y) {
    ll res = 1;
    if (y <= 0) return 1;
    while(y) {
        if (y & 1) res = res * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return res;
}
void solve()
{
    cin >> n ;
    for (int i = 1 ; i <= n ; i ++) {
        cin >> a[i];
        ve.pb(a[i]);
    }
    sort(ve.begin(), ve.end());
    ve.erase(unique(ve.begin(), ve.end()), ve.end());
    ll ans = 0;
    for (int i = n ; i >= 1 ; i --) {
        int pos = getid(a[i]);
        ans += getsum(pos-1);
        add(pos);
    }
    ans %= mod;
    ans = ans * qpow(2, n-2) % mod;
    cout << ans << endl;
}

你可能感兴趣的:(树状数组,比较杂的题解)