Codeforces Round #729 (Div. 2) D. Priority Queue (思维,递推)
-
题意:给你一个序列A,每个元素为\(+ \ x\)或者\(-\),假设\(S\)是\(A\)的一个子序列,那么便利\(S\),\(+\ x\)表示贡献加上\(x\),\(-\)表示贡献减去\(S\)中最小的\(x\),并且在序列中删去这个\(x\),问所有子序列的和为多少(\(\mod 998244353\).
-
题解:先假设一个子序列集合\(S\)中,最后的贡献有\(x\)这个数的贡献,也就是说我们这个\(+\ x\)一定存活到了最后,没有被减去,那么这个集合中,在\(x\)位置之前的且比\(x\)大的数是一定不会受到任何影响的,在\(x\)位置后且不小于\(x\)的数也一定是不会受到任何影响的,我们记这些数的数量为\(cnt\),那么他们的方案数就是\(2^{cnt}\).
然后我们再对剩下的数进行讨论,可以直接进行遍历,过滤掉上述情况的数,设\(dp[i]\)为最后剩下\(i\)个数的方案数.注意这里是不包含当前所选的这个\(x\)的.再特别注意,在\(i\)遍历到当前位置之前,如果出现\(-\),那么\(dp[0]*=2\),而过了当前位置之后,因为\(x\)是一定要选的,所以就不能再乘2了.剩下的就是一个线性的递推转移即可.
-
代码:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 998244353; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int n; char op[N]; ll a[N]; ll dp[N]; //除去无影响的数后,剩下i个数的方案数 ll pw[N]; int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>n; pw[0]=1; for(int i=1;i<=n;++i){ cin>>op[i]; if(op[i]=='+'){ cin>>a[i]; } pw[i]=pw[i-1]*2%mod; } ll ans=0; for(int k=1;k<=n;++k){ //全程不包含a[k]的情况 if(op[k]!='+') continue; ll cnt=0; for(int i=0;i<=n+5;++i) dp[i]=0; dp[0]=1; for(int i=1;i<=n;++i){ if(i<=k && a[i]>a[k]) cnt++; else if(i>k && a[i]>=a[k]) cnt++; } for(int i=1;i<=n;++i){ if((i<k && a[i]>a[k]) || (i>k && a[i]>=a[k]) || i==k) continue; if(op[i]=='-'){ if(i<k) dp[0]=(dp[0]*2)%mod; //因为a[k]是必须要选的 for(int j=0;j<n;++j) dp[j]=(dp[j]+dp[j+1])%mod; } else{ for(int j=n-1;j>=1;--j){ dp[j]=(dp[j]+dp[j-1])%mod; } } } for(int i=0;i<n;++i){ ans=(ans+pw[cnt]*dp[i]%mod*a[k])%mod; } } cout<<ans<<'\n'; return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮