1. 程式人生 > 其它 >AtCoder ABC 221 D

AtCoder ABC 221 D

原題連結

題目分析

不難分析出來,我們就是要求出,對每一個位置i而言,其後面的比它大的位置j1,j2.....jn,構成的數學式子求解。

\[ans[i]=2^{j_1-i-1}+2^{j_2-i-1}+...+2^{j_n-i-1} \]

我們可以對式子進行化簡。

\[ans[i]=\frac{2^{j_1-1}+2^{j_2-1}+...2^{j_n-1}}{2_i} \]

然後,emm,我就卡在這裡了,因為這時就要求出i之後所有比它大的位置。

但是,我忽略了一個很重要的思想,正著想不行的時候,我們可以嘗試反著想。

如果,我們要求的是一個數前面所有比它小的位置,會有出路嘛?

我們可以很快的發現,當要找一個數前面比他小的位置來求答案時,我們要求的式子就變成了

\[ans[j]=2^{j-1}(\frac{1}{2^{i_1}}+\frac{1}{2^{i_2}}+...+\frac{1}{2^{i_n}}) \]

那接下來要解決的問題就是,括號裡面的怎麼求?

這時,就簡單了,我們可以用樹狀陣列進行優化。在樹狀陣列中,考慮每個位置時,在w[j]上插入對應的式子。這樣正向掃描時,這樣sum(w[j])就是前面比它小的位置上就是對應的式子的和(即括號裡的內容)了。

AcCode

#include<iostream>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
const LL N = 3e5 + 10,mod=998244353;
LL tr[N];
int w[N];
int n;

int lowbit(int x)
{
    return x & (-x);
}

void add(int x,LL c)
{
    for(int i=x;i<=n;i+=lowbit(i))
    {
        tr[i]=(tr[i]+c)%mod;
    }
}

LL sum(int x)
{
    LL res = 0;
    for(int i=x;i;i-=lowbit(i))
    {
        res=(res+tr[i])%mod;
    }
    return res;
}

LL ksm(LL a,LL b)
{
    LL res = 1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return res;
}

void init()
{
    map<int,int> mp;
    for(int i=1;i<=n;i++)
        mp[w[i]]=0;
    int sz=0;
    for(auto &p:mp) p.second=++sz;
    for(int i=1;i<=n;i++) w[i]=mp[w[i]];
}

int main()
{
    const LL div = ksm(2,mod-2);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>w[i];
    LL ans = 0;
    init();
    for(int i=1; i<=n; i++){
        ans += sum(w[i])*ksm(2,i-1);
        ans %= mod;
        add(w[i],ksm(div,i));
    }
    cout<<ans<<endl;
    return 0;
}