开学大二cf补题(第一周)
开始cf征程
题意:给你一个字符串有加号有减号还有0 和 1 +代表给数组加一个数字在末尾 -代表末尾减一个数,0代表这个数组是一个严格降序的数组 1代表当前这个数组是一个升序数组
问你当他询问0或者1时符不符合条件(如果数组元素小于2那么就是1)
题解:这个首先定-1元素为待定状态 1为这个元素是用来升序的 0代表这个元素是用来打乱升序的就是让这个数组变成0
然后我们开两个栈 一个记录数组元素情况 一个是用来记录升序个数的
首先+那么我们就把-1到栈里这些都是待定
-我们就弹出队尾
遇到1时检查有多少个-1把他全变成1升序
然后判断(-1全部搞完后)如果遇到0就不行了,不是升序了就NO
如果遇到0 那么如果队尾是1就是升序情况,那我们就无法更改,只有-1这个待定可以改,也就是要打NO了
如果队首是-1那么我们把他变成0就是在升序结尾改一个小的数这样就符合题目条件的0了(刚好破坏1的条件)就是YES
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=600,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int32_t main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int t; cin>>t; while(t--) { string s; cin>>s; stack<int> st,ck; int f=0; for(int i=0;i<s.size();i++) { if(s[i]=='+') { st.push(-1); } else if(s[i]=='-') { st.pop(); } else if(s[i]=='1') { if(st.size()==1) continue; while (!ck.empty()) ck.pop(); while (!st.empty() && st.top()==-1) { st.pop(); ck.push(1); } if(!st.empty() && st.size()>1 && st.top()==0) { f=1; cout<<"NO"<<endl; break; } while (!ck.empty()) { st.push(ck.top()); ck.pop(); } } else { if(st.size()<2) { f=1; cout<<"NO"<<endl; break; } if(!st.empty() && st.size()>1 && st.top()==1) { f=1; cout<<"NO"<<endl; break; } st.pop(); st.push(0); } } if(f==0) { cout<<"YES"<<endl; } } return 0; }
题意:给你一个数组包含1到1e1000 然后给你一个下标数组,每次要删除下标数组里的对应元素,每一次下标都在更替,需要删除k次
问你删除k次后第1个元素是什么
借图理解
题解:首先我们可以模拟一下删除过程,在过程中我们发现第二个删除的下标不会干扰前面删除的数字同时也不干扰后面输出的数字,每个删除的数字相互独立,,然后发现会空出一个数字,在不考虑删除1的情况下,空出的数字就是1,刚好是一个变差数列,我们模拟这个过程即可
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=2e5+7,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int a[N]; void solve() { int n,k; cin>>n>>k; for(int i=1;i<=n;i++) cin>>a[i]; if(a[1]!=1) { cout<<1<<endl; return; } else { a[n+1]=1e18; int sum=1,d=1; int l=0,l1=0; for(int i=2;i<=n+1;i++) { while (sum+d<a[i]) { sum+=d; l=sum; l1++; if(l1==k) break; } if(l1==k) { break; } d=i; } cout<<l<<endl; } } signed main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int T=1; cin>>T; while(T--){ solve(); } return 0; }
题意:给你一个数组里面有n个数,然后给你一个二进制串,有两个操作,1操作时给你一个区间l和r在这个区间内对二进制串进行操作
0变成1 1变成0 2操作给你0或者1 如果是0在二进制串里找0,是0的下标记录,然后在数组里对应下标的数进行异或,输出以后结果
题解:这道题是一个思维+异或前缀和
首先我们搞一个异或前缀,后面想要在l到r区间反转我们直接
然后呢就是开一个数组记录1下标里面的元素的异或和0里面的元素的异或值
s里面只有0或者1嘛,我们直接对应他的下标用就行
然后我们就搞好了1下标值的异或值和0的
整一个程序不对二进制串进行操作,我们对数组进行操作的
那我们要是1操作反转之后这么搞,就是上面那个反转然后出一个t
这个t就是0 1反转后的值对于数组的0 1反转值
我们只要对全0下标元素异或一次t就是目前的结果了 1下标有同理
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=2e5+7,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int a[N]; int pr[N]; void solve() { int n; cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; } string s; cin>>s; s=' '+s; vector<int> b(3); for(int i=1;i<=n;i++) { pr[i]=pr[i-1]^a[i]; b[s[i]-'0']^=a[i]; } int q; cin>>q; while (q--) { int x; cin>>x; if(x==1) { int l,r; cin>>l>>r; int t=pr[r]^pr[l-1]; b[0]^=t; b[1]^=t; } else { int y; cin>>y; cout<<b[y]<<' '; } cout<<endl; } } signed main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int T=1; cin>>T; while(T--){ solve(); } return 0; }
题意:给你一个字符串里面只有(或者)然后你要改变一个括号就是把括号反过来,把这个字符串括号变成合理的,就是左右括号对应的
问你有几种改的方法 只能改一次
题解:首先只能改一个那么我们可以想到,然后括号不符合规则的个数大于2个那么就不可能搞的出来要么就是两个( 要么就是两个)
我们怎么搞呢,就是如果是(就++ , )这个就减减,然后我们看尾数是几就可以了只能是2或者-2的情况
然后就是还有一种情况比如(()))(((这种情况也是多二但是它是不合理的,答案是0
所以我们发现左括号在遍历的过程中不能小于又括号数量如果小于我们就需要跳出==0
总结:
1:经过加加,减减后如果是2或者-2就有解否则0
2:左括号在加减过程中不能小于0,否则直接跳出,不能加了
然后我们倒放把尾数往前推就是
这样只比如-2.我就把前面大于-2的全变成-2,2也是同理
然后就是2的时候并且是"("那么我们就会加入答案,-2也同理
然后就是第二种情况了 我们可以拿一个数K在里面加减 然后K如果小于0直接跳出即可
(注意如果这种情况下那我们的答案不会加减)
举例
(()))(((
1 2 1 0 -1 0 1 2
我们发现操作后
-1 -1 -1 -1 0 1 2
那么因为-1我们直接嘎没了
(())))
1 2 1 0 -1 -2
操作
-2 -2 -2 -2 -2 -2
看出区别了吗
然后在根据括号加减即可
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=2e5+7,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; void solve() { int n; cin>>n; string s; cin>>s; int ans=0; vector<int> a; for(int i=0;i<n;i++) { if(s[i]=='(') ans++; else ans--; a.push_back(ans); } if(ans!=2 && ans!=-2) cout<<0; else { int sum=0; int k=0; for(int i=n-2;i>=0;i--) { a[i]=min(a[i],a[i+1]); } for(int i=0;i<n;i++) { // cout<<a[i]<<' '; if(s[i]=='(') { if(ans==2 && a[i]>=2) sum++; k++; } else { if(ans==-2 && a[i]>=-2) sum++; k--; } if(k<0) break; } cout<<sum; } } signed main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int T=1; // cin>>T; while(T--){ solve(); } return 0; }
P1570 KC 喝咖啡 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:有n杯咖啡 你可以选m个调料
每一个调料制作有一个c时间
每一个调料有一个美味值v
现在问你,你可以选m个调料然后总和(v)/总和(c)要最优是多少
题解:这道题是一个二分题我们可以枚举答案
首先确定边界r是一个最大值,也就是v/c的最大,直接枚举即可
然后二分答案
但是怎么搞check呢
我们可以转换公式 答案=v/c == 答案*c-v==0
所以我们发现只要让答案趋于0直到等于0就是答案
所以我们计算每一个小料的权值(vc=x*c-v)然后从小加到大
如果t>=0那么说明答案太大我们变r
r<0就是变l就可以了
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=1000,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int n,m; struct G{ int v,c; double vc; }a[N]; bool cmp(G x,G y) { return x.vc<y.vc; } bool ch(double x) { for(int i=1;i<=n;i++) { a[i].vc=x*a[i].c-a[i].v; } sort(a+1,a+1+n,cmp); double t=0; for(int i=1;i<=m;i++) { t+=a[i].vc; } if(t>=0) return true; else return false; } void solve() { cin>>n>>m; for(int i=1;i<=n;i++) { cin>>a[i].v; } double l=0,r=0; for(int i=1;i<=n;i++) { cin>>a[i].c; double x=a[i].v*1.0/a[i].c*1.0; if(x>r) r=x; } while (r-l>1e-6) { double mid=(l+r)/2.0; if(ch(mid)) r=mid; else l=mid; } printf("%.3lf",l); } signed main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int T=1; // cin>>T; while(T--){ solve(); } return 0; }
这题构造题,赛时愚钝了一下,差点出
题意:让你构造一个矩阵n*m(元素只能放m-1个排列),这个矩阵的每一列取一个MAX(就是选最小的没出现的正整数)
每一列取完然后每一列取出的MAX元素在取一个MAX
让你求最大的MAX
题解:首先我们先考虑n<m的情况,这种情况下我们发现就是根本不能把m-1个元素放完,就是说不能取到最大值m
然后我们发现它是一个往下掉的,就是n越小可以取的越小,因为放元素只能放那几个,所以就是n+1最大值
而n>=m的时候那么就是可以搞最大值m了
就是排列放法
比如·m=4
0123
1230
2301
0123
我们可以发现就是以m-1为循环节,一位一位往后推即可,这样无论如何都是最大值
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=2e5+9,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; void solve() { int n,m; cin>>n>>m; if(n<m) { cout<<n+1<<endl; } else if(m==1) { cout<<0<<endl; for(int i=0;i<n;i++) cout<<0<<endl; return; } else { cout<<m<<endl; } int f=0; int x=0; for(int i=0;i <n;i++) { if(x%(m-1)==0) x=0; f=x; for(int j=0;j<m;j++) { cout<<f<<' '; if(f==m-1) { f=-1; } f++; } x++; cout<<endl; } } signed main(){ std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int T=1; cin>>T; while(T--){ solve(); } return 0; }
本周到此结束,加油吧!!!!