一、题目描述

  给定一个序列 $A$,$A$ 的每一个元素形如 $+x$ 和 $-$,其中 $x$ 为一个整数($1\le x< 998244353$)。

  对于 $A$ 的一个子序列 $S$,按如下方式计算 $S$ 的权值:

    你需要依次遍历 $S$ 中的元素,并且按照序列维护一个小根堆 $T$。

    特别的,若 $T$ 中没有数,那么就不进行删除操作。

    在遍历完 $S$ 中的元素后,将小根堆 $T$ 中所有数的和 $sum$ 算出来。

    $sum$ 即为 $S$ 的权值。

  求 $A$ 中所有子序列 $S$ 的权值和。对 $998244353$ 取模。

  数据范围:$1\le n\le 500$。


 二、解题思路:

  掌握一个算贡献的方法:$\sum_A\sum_{x\in A}=\sum_xx\times \sum_A[x\in A]$

  于是我们可以很容易的想到单独对于每个 $+x$ 统计贡献。

  然后这个是非常容易计数 $dp$ 的,设目前我们枚举的位置为 $p$。值为 $val$。

  $f_{i,j}$ 表示目前枚举到序列的前 $i$ 个元素,小根堆中有 $j$ 个数字小于 $val$ 的子序列个数。

  转移很好转移,注意 $i$ 与 $p$ 的相对位置来进行讨论。

  相同的数字大小关系定一个标准就行了,比如下标小的就视为更小。

  单次 $dp$ 时间复杂度 $O(n^2)$,总时间复杂度 $O(n^3)$。


 三、完整代码

 1 #include<iostream>
 2 #define N 510
 3 #define mod 998244353
 4 #define ll long long
 5 #define rep(i,l,r) for(ll i=l;i<=r;i++)
 6 using namespace std;
 7 ll n,ans;
 8 ll f[N][N];
 9 ll op[N],a[N];
10 void add(ll &u,ll v){
11     u+=v;
12     if(u>=mod) u%=mod;
13 }
14 ll calc(ll p){
15     ll val=a[p]; f[0][0]=1;
16     rep(i,1,n) rep(j,0,i) f[i][j]=0;
17     rep(i,1,n){
18         rep(j,0,i) f[i][j]=f[i-1][j];
19         if(i<p){    
20             if(!op[i]){
21                 rep(j,0,i) add(f[i][j],f[i-1][j+1]);
22                 add(f[i][0],f[i-1][0]);
23             }else{
24                 if(a[i]>val){
25                     rep(j,0,i) add(f[i][j],f[i-1][j]);
26                 }else{
27                     rep(j,1,i) add(f[i][j],f[i-1][j-1]);
28                 }
29             }
30         }else if(i>p){
31             if(!op[i]){
32                 rep(j,0,i) add(f[i][j],f[i-1][j+1]);
33             }else{
34                 if(a[i]>=val){
35                     rep(j,0,i) add(f[i][j],f[i-1][j]);
36                 }else{
37                     rep(j,1,i) add(f[i][j],f[i-1][j-1]);
38                 }
39             }
40         }
41     }
42     ll res=0;
43     rep(i,0,n) add(res,f[n][i]);
44     return res;
45 }
46 signed main(){
47     ios::sync_with_stdio(false);
48     cin.tie(0); cout.tie(0);
49     cin>>n;
50     rep(i,1,n){
51         char x; cin>>x;
52         if(x=='+') op[i]=1,cin>>a[i];
53     }
54     rep(i,1,n) if(op[i]) add(ans,calc(i)*a[i]);
55     cout<<ans<<'\n';
56     return 0;
57 }

四、写题心得:

  复竞了,感觉手好生。只有写点简单题找一下感觉了。

  不过这个题还是很有意思啊!拜谢 $lsy$ 大佬。省选加油!

posted on 2024-01-30 09:53  trh0630  阅读(12)  评论(3编辑  收藏  举报