牛客OI周赛15-普及组
链接:https://ac.nowcoder.com/acm/contest/4911/A
来源:牛客网
题目描述
牛牛最近喜欢玩咪咪游戏,于是自己写了个程序编了个游戏让牛妹来玩。游戏是这样的:
牛牛有一个长的字符串(只包26含个小写字母),他想让牛妹判断这个字符串是好的。
定义一个串是好的:这个串是由连续的mq连接而成的。
比如mqmq{mqmq}mqmq说明这个串是好的,mqmqm{mqmqm}mqmqm或mqmqx{mqmqx}mqmqx都是不好的。现在牛牛 想问牛妹这个串是否是好的,如果好的输出Yes{Yes}Yes,否则输出No{No}No
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<set> #include<map> using namespace std; const long long p=1e9+7; #define maxn 700000 long long ans,n,k; char a[maxn]; int main() { cin>>n; while(n--) { int ok=0; memset(a,0,sizeof(a)); cin>>a; for(int i=0;i<strlen(a);i+=2) { if(a[i]!='m'||a[i+1]!='q'){ ok=1; cout<<"No"<<endl;break; } } if(!ok) cout<<"Yes"<<endl; } }
链接:https://ac.nowcoder.com/acm/contest/4911/B
来源:牛客网
题目描述
上天眷顾了牛牛,给牛牛n个宝盒。牛牛会从这n个箱子中各取一件宝物去当掉来换钱(每个箱子中有mi件宝物)。
牛牛想知道他用不同的方法取宝物能当来的钱数量的前k小值,为了避免输出量过大只要输出∑ai(i≤k)\sum a_i (i \leq k)∑ai(i≤k)即可,其中的 ai为第i小值。
有种分组背包的感觉;当时看到第i小就想到了线段树,当然不知道怎么写!!题解很棒,dp【i】【j】从第i个箱子中取出第j个物品,此时的方法数!!(我感觉我好想写过
这篇题解),算出最大能取出的值sum,接下来有点类似背包;i从1到n枚举,到第i个背包时的方法,dp【i】【j】+=dp【i-1】【j-w[i]】;
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<set> #include<map> using namespace std; const long long p=998244355; #define maxn 20000 #define lson ((pos)*2) #define rson ((pos)*2+1) int m,ans,sum,dp[200][maxn],a[200][maxn],n; int main() { cin>>n>>m; for(int i=1;i<=n;i++) { int tot=0; cin>>a[i][0]; for(int j=1;j<=a[i][0];j++) cin>>a[i][j],tot=max(a[i][j],tot); sum+=tot; } dp[0][0]=1; for(int i=1;i<=n;i++) { for(int h=1;h<=a[i][0];h++)//第i个背包的物品数 for(int j=sum;j>=a[i][h];j--)//有多少种方法 dp[i][j]+=dp[i-1][j-a[i][h]]; }//最后选择dp[n][] int coun=0; for(int i=1;i<=sum;i++) { while(dp[n][i]){ ans+=i; dp[n][i]--; coun++; if(coun==m) { cout<<ans;return 0; } } } return 0; }
链接:https://ac.nowcoder.com/acm/contest/4911/C
来源:牛客网
题目描述
牛牛给牛妹出了到简单区间加题,牛妹觉得太简单于是稍微修改了一下。
牛妹有个长为n{n}n的序列A{A}A。每次牛妹会选择这个序列中的一段区间进行区间加1{1}1,但是要满足的条件:对于任意两次区间加的起始,结束位置各不相同。
现在牛妹问你存在多少种区间加方式使得最后得到的序列使得∀Ai=m{∀ A_i=m}∀Ai=m,由于方案数过多,答案对998244355取模。
牛妹有个长为n{n}n的序列A{A}A。每次牛妹会选择这个序列中的一段区间进行区间加1{1}1,但是要满足的条件:对于任意两次区间加的起始,结束位置各不相同。
现在牛妹问你存在多少种区间加方式使得最后得到的序列使得∀Ai=m{∀ A_i=m}∀Ai=m,由于方案数过多,答案对998244355取模。
这道题很nb!并没想到用()来表示,尽管有人数,样例的解释就是用括号,可以推出。
所求答案为方案数,不是区间修改,不是求和之类的,又没明确单调性,就考虑dp吧。按题解,用括号表示,首先需要合法,即左括号小于等于右括号的数目;
dp【i】【j】(i表示到达第i个数时,“(”比 “)”多j个);
then就需要分情况找状态转移方程,对于某个位置i,可以放置左括号或右括号,(和),以及不放
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<set> #include<map> using namespace std; const long long p=998244355; #define maxn 5000 #define lson ((pos)*2) #define rson ((pos)*2+1) long long ans,n,m,k,l; long long a[maxn],dp[maxn][maxn]; int main() { cin>>n>>m; bool ok=0; for(int i=1;i<=n;i++){ cin>>a[i];if(a[i]>m) ok=1;} if(ok) cout<<0<<endl; else { if(a[1]==m) dp[1][0]=1; if(a[1]==m-1) dp[1][1]=1,dp[1][0]=1; for(int i=2;i<=n;i++) { dp[i][m-a[i]]=(dp[i][m-a[i]]+dp[i-1][m-a[i]-1])%p; dp[i][m-a[i]-1]=(dp[i][m-a[i]-1]+dp[i-1][m-a[i]-1]*(m-a[i]))%p; dp[i][m-a[i]]=(dp[i][m-a[i]]+dp[i-1][m-a[i]])%p; dp[i][m-a[i]-1]=( dp[i][m-a[i]-1]+dp[i-1][m-a[i]]*(m-a[i]))%p; } cout<<dp[n][0]; } return 0; }