ABC221E LEQ

ABC221E(树状数组+乘法逆元)

题目链接

Problem Statement

Given is a sequence of N N N integers: A A A= ( A 1 A_1 A1, A 2 A_2 A2, … \dots , A N A_N AN)

Find the number of (not necessarily contiguous) subsequences A ′ = ( A 1 ′ , A 2 ′ , … , A k ′ ) A'=(A'_1,A'_2,\ldots,A'_k) A=(A1,A2,,Ak) of length at least 2 2 2 that satisfy the following condition:

  • A 1 ′ ≤ A k ′ A'_1 \leq A'_k A1Ak.

Since the count can be enormous, print it modulo 998244353 998244353 998244353.

Here, two subsequences are distinguished when they originate from different sets of indices, even if they are the same as sequences.

Constraints

  • 2 ≤ N ≤ 3 × 1 0 5 2 \leq N \leq 3 \times 10^5 2N3×105
  • 1 ≤ A i ≤ 1 0 9 1 \leq A_i \leq 10^9 1Ai109
  • All values in input are integers.

题意

​ 给你长度为 N N N的数组,问你这个数组中有多少个非下降子序列(长度至少为 2 2 2)。

思路

​ 如果一个序列即有上升的与下降的,这样我们很难看出来怎么做。那么我们就会去考虑如果是一个非下降序列这么去求非下降子序列个数的。对于一个非下降序列 ( a i , a i + 1 , … , a j ) (a_i,a_{i+1},\dots,a_j) (ai,ai+1,,aj)来说,固定头 a i a_i ai与固定尾 a j a_j aj一共会有 2 j − i − 1 2^{j-i-1} 2ji1个非下降子序列,因为中间的都只有两种选择:选,或者不选,中间的数一共有 j − i − 1 j-i-1 ji1,因此共有 2 j − i − 1 2^{j-i-1} 2ji1次选择。那么我们就可以枚举非下降序列的末端,然后再去枚举其非下降序列的头,乘上 2 l e n − 2 2^{len-2} 2len2,公式为
∑ j = 1 n ∑ i = 1 j − 1 2 j − i − 1 ( a i ≤ a j ) j 为序列的末端点, i 为序列的首端点 \sum_{j=1}^n\sum_{i=1}^{j-1}2^{j-i-1}(a_i\le a_j) \\ j为序列的末端点,i为序列的首端点 j=1ni=1j12ji1(aiaj)j为序列的末端点,i为序列的首端点
​ 但是我们发现上述公式实现起来的复杂度为 O ( n 2 ) O(n^2) O(n2),这明显会 T T T.

​ 我们就可以想到树状数组,树状数组可以去查询序列中在元素左边比该元素小的元素个数,那么我们就可以将这个魔改一下,使其结点权值增加为 2 − i 2^{-i} 2i,然后去遍历一遍就行了。

#include<bits/stdc++.h>
#define Case int t;scanf("%d",&t);while(t--)
using namespace std;
typedef long long ll;
const int N=1e6+10;
const int M=1e6+10;
const ll mod=998244353;
char s[30];
ll a[N],b[N];
ll ans[N];
ll tr[N];
int n;
ll dp[1005][1005];
ll lowbit(ll x){
   return x&(-x);
}
ll qpow(ll q,ll n){
   ll res=1;
   while(n){
      if(n&1){
         res=(res*q)%mod;
      }
      n>>=1;
      q=(q*q)%mod;
   }
   return res;
}
void add(ll x,ll c){
   for(ll i=x;i<=n;i+=lowbit(i)){
      tr[i]+=c;
   }
}
ll query(ll x){
   ll ans=0;
   for(ll i=x;i;i-=lowbit(i)){
      ans=(ans+tr[i])%mod;
   }
   return ans;
}
void run(){
   scanf("%d",&n);
   for(int i=1;i<=n;i++){
      scanf("%lld",&a[i]);
      b[i]=a[i];
   }
   sort(b+1,b+1+n);
   ll idx=unique(b+1,b+n+1)-b;   
   for(int i=1;i<=n;i++){
      a[i]=lower_bound(b+1,b+idx,a[i])-b;
   }
   ll ans=0;
   for(int i=1;i<=n;i++){
      ans=(ans+qpow(2,i-1)*query(a[i])%mod)%mod;
      add(a[i],qpow(qpow(2,mod-2),i)); 
   }
   printf("%lld\n",ans);
}
int main(){
   // Case
   run();
   return 0;
}

​ 道阻且长,且行且珍惜

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值