牛客周赛 round 82 A--F
A.夹心饼干
一个string
#include<bits/stdc++.h> using namespace std; int main(){ string s; cin>>s; if(s[0]==s[2]) cout<<"YES"; else cout<<"NO"; return 0; }B.食堂大作战1.0
一个set求size()
#include<bits/stdc++.h> using namespace std; const int N=1e6+200; int a[N]; set<int>st; int main(){ int n; cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; st.insert(a[i]); } if(st.size()==n) cout<<"YES"; else cout<<"NO"; return 0; }C.食堂大作战2.0
一个set求size()+sort()排序即可
#include<bits/stdc++.h> using namespace std; const int N=1e6+200; struct node{ int num,pos; }d[N]; set<int>st; int cmp(struct node a,struct node b){ return a.num<b.num; } int main(){ int n; cin>>n; for(int i=1;i<=n;i++){ cin>>d[i].num; d[i].pos=i; st.insert(d[i].num); } if(st.size()!=n) cout<<"NO"; else{ cout<<"YES"<<endl; sort(d+1,d+1+n,cmp); for(int i=1;i<=n;i++){ cout<<d[i].pos<<" "; } } return 0; }D.小苯的排列计数
一个不合法的序列具有以下三种中至少一种的情况
1.序列的最后一位不为1
2.序列的某一位比上一位大(除了第一位)
3.序列中出现可选的数字个数<需要选的数字个数的情况(如:4 3 3 1,明显第三位数字选不了)
剩下的都是合法序列
对于合法序列求总方案数,其中一种方法是:每一个不确定的数的位置上,可以填的数字个数的总和相乘
求当前位置的不确定的数(计算当前位置可选的数的数量):即为:总的区间大小-已经确定的数个数-不确定的数中已经被选过的数个数
总的区间:设当前位置为x,那么其可以选的数的下界为p[x],上界为n,这其中包含(n-p[x]+1)个数
已经确定的数:用vector<int>a存储确定的数,易得,若序列的某一位比上一位小,那么这一位数字唯一确定
不确定的数中已经被选过的数:设一个变量presum,每一次将用过的数的个数加入presum里即可(此处也可以预处理,用vector<int>tmp存储某一段区间内需要的数个数,与a同步记录)
具体实现可以看下面代码,基本的关键语句都有注释
(注意取模!)
#include<bits/stdc++.h> #define int long long int using namespace std; const int N=1e6+200; int MOD=998244353; int p[N]; void solve() { int n,ans=1; cin>>n; for(int i=1; i<=n; i++) { cin>>p[i]; } if(p[n]!=1) { //如果p数组的最后一个值不是1,那一定不合法 cout<<"0"<<endl; return ; } for(int i=2; i<=n; i++) { //如果p数组存在后面的数字大于前面的数字的情况,也不合法 if(p[i]>p[i-1]) { cout<<"0"<<endl; return ; } } vector<int>a,tmp; int pos=1; while(pos<=n) { //往a数组里面存入已经确定的数字 a.push_back(p[pos]); //第一个数字一定确定,存入a int cnt=0,nxt=pos+1; while (nxt<=n && p[nxt] == a.back()) { //如果a数组中的最后一个数仍然是目前的最小值 cnt++; //需要选的数字个数+1 nxt++; //目前位置+1 } tmp.push_back(cnt); //tmp数组存储对应需要选的数字个数 pos=nxt; } int prenum=0; //persum:前面选过的数字总个数 int si=a.size(); for(int i=0; i<si; i++) { //注意此处遍历的是a int tmpnum=(n-a[i]+1)-(i+1)-prenum; //计算可选的数字个数 if (tmpnum<tmp[i]) { //可选的数字个数<需要选的数字个数 ans=0; break; } for (int j=0; j<tmp[i]; j++) { //计算目前这一段数字对于ans的贡献 ans=ans*(tmpnum-j)%MOD; } prenum+=tmp[i]; //更新前面选过的数字总个数 } cout<<ans%MOD<<endl; return ; } signed main() { int T; cin>>T; while(T--) { solve(); } return 0; }
(下面的分析默认数组内第一个元素存放的下标为1)
对于a数组,新开一个minsuma数组,其中minsuma[x]表示a数组在[1,x]内取m个数的最小值
对于b数组,新开一个minsumb数组,其中minsumb[x]表示b数组在[x,n]内取m个数的最小值
对于求minsuma[x]与minsumb[x],一种比较简单的方法是:开两个优先队列quea,queb,分别用于存储目前a数组与b数组的m个最小值
对于a,当遇到下一个元素时,如果这一个元素小于quea.top(),那么将这一个元素存入队列,否则不存,对于b同理
最后的答案就是ans=min(ans,minsuma[i]+minsumb[i+1]); (i的范围为[m,n-m]),相当于求前i个数内取a的m个数,第(i+1)个数到第n个数内取b的m个数
具体实现可以看下面代码
(P.S.:请仔细分析下标的范围,不要大了或小了)
#include<bits/stdc++.h> #define int long long int using namespace std; const int N=2e5+200; int a[N],b[N],minsuma[N],minsumb[N]; priority_queue<int>quea; priority_queue<int>queb; signed main() { int n,m; cin>>n>>m; for(int i=1; i<=n; i++) { cin>>a[i]; } for(int i=1; i<=n; i++) { cin>>b[i]; } for(int i=1; i<=m; i++) { minsuma[m]+=a[i]; quea.push(a[i]); } for(int i=m+1; i<=n-m; i++) { int tmp=quea.top(); if(a[i]<tmp) { quea.pop(); quea.push(a[i]); minsuma[i]=minsuma[i-1]-tmp+a[i]; }else{ minsuma[i]=minsuma[i-1]; } } for(int i=n; i>=n-m+1; i--) { minsumb[n-m+1]+=b[i]; queb.push(b[i]); } for(int i=n-m; i>=m+1; i--) { int tmp=queb.top(); if(b[i]<tmp) { queb.pop(); queb.push(b[i]); minsumb[i]=minsumb[i+1]-tmp+b[i]; }else{ minsumb[i]=minsumb[i+1]; } } int minn=1e18; for(int i=m;i<=n-m;i++){ minn=min(minn,minsuma[i]+minsumb[i+1]); } cout<<minn<<endl; return 0; }
F.怎么写线性SPJ
具体分析思路请看:牛客周赛 Round 82 解题报告 | 珂学家_牛客博客
画出来之后发现这个数字的排序像是一棵树,n取几就从左往右取几个数输出,所以可以用lowbit(x),即x&(-x)实现
下面代码中的__lg(x)函数能够实现对于log2(x)的向下取整功能;
#include<bits/stdc++.h> using namespace std; int main(){ int n; cin>>n; cout<<__lg(n)+1<<endl; for(int i=1;i<=n;i++){ cout<<__lg(i&(-i))+1<<" "; } return 0; }(P.S.:最后一题看了半小时没看出来的我真的很小丑)