A
题意其实有点迷(其实是自己读题没认真)
就是只要问字符串是不是由若干个"mq"组成
所以长度奇数的肯定就是No(不过数据貌似没有mqmqm这种?试了下竟然ac了)
否则就去看奇数位置是不是m 偶数位置是不是q 都是的话就Yes 不然就No
#include<bits/stdc++.h> using namespace std; int main(){ int q;cin>>q; while(q--){ string s;cin>>s; int len=s.size(); if(len&1) {puts("No");continue;} int flag=0; for(int i=0;i<len;i++){ if(i&1 && s[i]!='q') flag=1; else if(!(i&1) && s[i]!='m') flag=1; } puts(flag?"No":"Yes"); } return 0; }
B
背包计数问题
dp[i][j]表示对于前i个宝箱,能装的价值为j的方案数有多少
转移的话就很简单了 dp[i][j]+=dp[i-1][j-v] v为当前宝物的价值 因为只能选一个 类比01背包 所以j倒序
最后只要计算Σ(dp[i][j]*j)即可 其中(Σj)=k
#include<bits/stdc++.h> using namespace std; int dp[105][105*105],a[105]; int main(){ ios::sync_with_stdio(0); int n,m;cin>>n>>m; dp[0][0]=1; for(int i=1;i<=n;i++){ int x;cin>>x; for(int j=1;j<=x;j++){ int q;cin>>q; for(int k=10000;k>=q;k--) dp[i][k]+=dp[i-1][k-q]; } } int ans=0; for(int i=1;i<=10000;i++){ if(dp[n][i]<m){ ans+=dp[n][i]*i; m-=dp[n][i]; } else { ans+=m*i; break; } } cout<<ans<<endl; return 0; }
C
序列这种区间加的问题,还是先考虑下差分,首先让a[i]=m-A[i]
算出每个位置离m还需要进行多少次操作,
然后进行差分a[i]-a[i-1] 这样算出来位置i比位置i-1需要多操作几次或者少操作几次
这里注意一下 差分之后的值b[i] 如果>1 或者<-1,那么直接输出0即可。
想一下,如果我这个位置比上一个位置多了2,意味着i位置这里比i-1位置多进行了两次操作,
怎么多呢,肯定是在i位置放一个左括号,但是因为一个位置左括号只能放一个,
如果还想让i位置操作次数增加,就只能放在i位置前面了,也就是放在≤i-1的位置
那这样这个左括号又同时覆盖了i-1和i位置,所以如果差分数组abs(b[i])>1 直接输出0即可
那么剩下了三种情况我们需要维护一个变量now来表示从左往右当前的左边为匹配的左括号数量
一、如果a[i]==1,意味着i比i-1要多操作一次,所以在i这个位置放一个左括号,未配对左括号多了一个,now++。
二、如果a[i]==-1,意味着i比i-1要少操作一次,所以在i-1这个位置放一个右括号,这里就要去计算下答案ans=ans * now
意思就是看i-1这个右括号,在前面now个左括号选一个配对,此时左括号配对了一个,now--。
三、a[i]==0,说明i和i-1操作次数一样,那么我们可以什么都不动,也可以在i处放一个右括号,所以方案数就是ans=ans * (now+1),now个左括号可以选择,还有一种不进行操作的可能。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=998244355; ll a[2005],b[2005]; int main(){ ios::sync_with_stdio(0); int n,m;cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i],a[i]=m-a[i],b[i]=a[i]-a[i-1]; ll ans=1,now=0; for(int i=1;i<=n;i++){ if(abs(b[i]>1)) return cout<<0,0; if(b[i]==1) ++now; if(b[i]==-1) (ans*=now)%=mod,now--; if(b[i]==0) (ans*=(now+1))%=mod; } cout<<ans<<endl; return 0; }
D
就是一个长度为n的序列,求长度为k的递增子序列个数呗?
(话说这不是原题嘛? https://ac.nowcoder.com/acm/contest/3566/C 原题的链接 改个模数和树状数组大小都过了)
仔细地说一下吧
首先考虑的是dp
dp[i][j]表示以a[i]结尾的长度为j的个数
转移也很简单 就是dp[i][j]+=dp[k][j-1] && a[i]>a[k] 代码如下
dp[0][0]=1; for(int i=1; i<=n; i++) { for(int j=0; j<i; j++) { for(int kk=1; kk<=k; kk++) { if(a[i]>a[j]) dp[i][kk]=(dp[i][kk]+dp[j][kk-1])%mod; } } }
这样子复杂度是n * n * k 高的批爆 肯定不行
接下来我们考虑优化 对于长度k这一层肯定是不行的,肯定就是从k-1个转移到k个来
对于a[i]>a[j]这一步而言 a[i]需要和他前面所有的数进行比较大小
所以 考虑树状数组维护已经计算过的以a[i]结尾的的长度为k的方案数
那么我们去维护k个树状数组,对于每次的长度k,我们去计算长度为k-1的树状数组中 结尾<a[i]的方案数
长度为k-1,最后一个数还<a[i],那我把a[i]接到后面,不就形成了以a[i]结尾的长度为k的方案数嘛
所以长度为k的以a[i]结尾的方案数 = 长度为k-1的树状数组中 结尾<a[i]的方案数
然后我们把长度为k的以a[i]结尾的方案数更新到维护的长度为k的树状数组上即可
最后我们只要计算维护长度为k的树状数组中所有的方案数总和就是答案了
复杂度 n * logn *k
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1e9+7; const int maxn=1e5+5; ll c[maxn][52],a[maxn],b[maxn]; void add(ll x,ll k,ll v){ for(;x<maxn;x+=x&(-x)) (c[x][k]+=v)%=mod; } ll sum(ll x,ll k){ ll ans=0; for(;x;x-=x&(-x)) (ans+=c[x][k])%=mod; return ans; } int main(){ ios::sync_with_stdio(0); int n,k;cin>>n>>k; for(int i=1;i<=n;i++) cin>>a[i],b[i]=a[i]; sort(b+1,b+1+n); int m=unique(b+1,b+1+n)-b-1; for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+m,a[i])-b; for(int i=1;i<=n;i++){ add(a[i],1,1); for(int j=2;j<=k;j++){ add(a[i],j,sum(a[i]-1,j-1)); } } cout<<sum(n,k)<<endl; return 0; }