赞
踩
五一假期,记vp一场c++c组的题目..
题意:给a个2*2的方格,b个1*1的方格,问能凑成的最大正方形边长;
思路:4a+1b开根号就是答案,前提是b得是偶数且是四的倍数,不妨先将b个1*1的方格凑成2*2的,然后2*2的格子数*4后开方,就是答案;
答案:5435122
题意:给2000行记录,求最长连续匹配数,匹配是指:当前记录中 第一二个字符一样 且和上一条记录(同匹配)之间的时间戳差值小于1s;
思路:略
答案:9
题意:给一个数列,问数列中有多少个数是属于一个前n项和的结果(n>=2)。
思路:数据范围显然不可能是枚举和,很可能需要位运算,如图,打表找规律,发现只有2的整数次幂才不满足,2的54次方刚好大于了的数据范围1e16, 所以只需要对枚举54位,数列中恰好等于k次方的数的个数就是答案。
复杂度:O(N)
- #include <iostream>
- #include <algorithm>
- #include <cmath>
- using namespace std;
- typedef long long ll;
-
- int main(){
- ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- int m,res = 0; cin >> m;
- for (int i = 1; i <= m; i ++){
- ll x; cin >> x;
- for (int j = 0; j <= 54; j ++){
- if (x == pow(2,j)) res ++;
- }
- }
- cout << res <<"\n";
-
- return 0;
- }
题意:给10以内数字加一个基础属性,0,4,6,9数字属性值是1,8的属性值是2,1,2,3,5,7的属性值是0,现给一个数列,要求按照属性值大小排序,若属性值大小相同,则输出数字本身大小
思路:结构体基础知识,重构小于号(手写sort的cmp也可以),直接把一个数字按数位拆出来然后叠加属性值就可以了,最后将数列按从小到大顺序输出。
复杂度:O(N)
- #include <iostream>
- #include <algorithm>
- #include <cstring>
- using namespace std;
- int n;
- const int N = 2e5+10;
- struct Nod{
- int w;
- bool operator < (const Nod &k) {
- int r1=0,r2=0;
- int m1 = w, m2 = k.w;
- while (m1){
- int x = m1%10;
- r1+=((x==0)||(x==4)||(x==6)||(x==9));
- r1+=((x==8)*2);
- m1/=10;
- }
- while (m2){
- int x = m2%10;
- r2+=((x==0)||(x==4)||(x==6)||(x==9));
- r2+=((x==8)*2);
- m2/=10;
- }
- if (r1==r2) return w < k.w;
- return r1 < r2;
- }
- }arr[N];
-
- int main(){
- ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- cin >> n;
- for (int i = 1; i <= n; i ++) cin >> arr[i].w;
- sort(arr+1, arr+n+1);
- for (int i = 1; i <= n; i ++) cout << arr[i].w << " ";
- return 0;
- }
题意:给一个数列,有两个操作,一是将单个数字+1或-1,二是将相邻的两个数字同时+1或者-1,问最后将其修改为回文数列的最小修改次数。
思路:回文相关的问题一般都具有对称性,考虑第i个数和考虑第n-i+1个数问题一样,可以忽略后者,因此这里只需要处理前一半。贪心,假设现在处理的第j个数与第n-j+1个数的差值 和 即将处理的第j+1个数与第n-(j+1)+1个数的差值同号,那么就可以用操作二一次性处理,剩下的就只能是操作1了。
复杂度:O(N)
- #include <iostream>
- #include <algorithm>
- #include <cstring>
- using namespace std;
- typedef long long ll;
- const int N = 2e5+10;
- ll a[N],p[N];
- ll res = 0;
- int main(){
- ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- int n; cin >> n;
- for (int i = 1; i <= n; i ++) cin >> a[i];
- for (int i = 1, j = n; i < j; i ++, j --) p[i] = a[j] - a[i];
- for (int i = 1; i <= n/2; i ++){
- res += abs(p[i]); // 异号单独加
- if (p[i]>0 && p[i+1]>0) p[i+1]-=min(p[i],p[i + 1]); // 同号一起处理
- if (p[i]<0 && p[i+1]<0) p[i+1]-=max(p[i],p[i + 1]);
- }
- cout << res << "\n";
- return 0;
- }
题意:给一个全0数列,有m次操作,每次给数列中一段区间+1,求分别撤销第i次操作后,数列中0的个数。
思路:差分+前缀和。先差分所有操作,然后记录一下当前数列当中0的个数r0,因为差分后是0的数,无论撤销哪一次操作,都不会变。随后,结合题意,假定撤销某次区间操作之后,那么也就意味着在这个区间操作被撤销之前必定是1,显然,问题可以转化为:求m个区间当中1的个数。将差分后的数组中的1拎出来做前缀和,然后分别算每个操作区间当中1的个数就可以了,记得每次都要加上r0。
复杂度:O(M+N)
- #include <iostream>
- #include <algorithm>
- #include <cstring>
- using namespace std;
- int n,m;
- const int N = 300010;
- typedef pair<int,int> PII;
- PII ts[N];
- int bs[N],pre[N];
- int ret = 0;
- int main(){
- ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- cin >> n >> m;
- for (int i = 1; i <= m; i ++){
- cin >> ts[i].first >> ts[i].second;
- bs[ts[i].first]++,bs[ts[i].second+1]--;
- }
- for (int i =1;i<=n;i++) bs[i]+=bs[i-1], ret += (bs[i]==0);
- for (int i =1;i<=n;i++) pre[i]+=pre[i-1]+(bs[i]==1);
- for (int i = 1; i <= m; i ++){
- int l = ts[i].first, r = ts[i].second;
- cout << pre[r]-pre[l-1]+ret <<"\n";
- }
- return 0;
- }
题意:在一维数轴上,给若干个指定坐标x和m,从起始点出发,每次可以朝左边/右边走一个单位,问不超过距离m最多能走到多少个不同的指定坐标。
思路:先预处理一个往左右两边走分别能包含坐标数的前缀和,g[0][i]表示从起始点往左走i个单位长度能包含多少个指定坐标,g[1][i]表示从起始点往右走i个单位长度能包含多少个指定坐标。
接着分析,假设有(小于0的情况也一样,对称的),若能被包含到答案里面,那么一定是从走过来,而不可能从走过来,这里也不难证明,假设从到的路线是优的,那从起始点出发到是优的、从起始点到也是优的,显然与前一命题产生矛盾,所以每一个指定坐标只需要考虑是否能从比自己小的坐标走过来。
其次讨论不同情况:情况1:从起始点一直往左走能找到解,答案就是g[0][m];情况2是情况1的镜像,答案是g[1][m]; 情况3:从起始点往左走i个单位,然后往回到起点再往右走()个单位;情况4也是情况3的镜像。
可以发现,情况1、2其实是可以和3、4合并的(情况3、4枚举往左/右走的单位长度,当单位长度是m的时候其实就是情况1和2,能往反方向走当且仅当)。
复杂度:O(N)
- #include <iostream>
- #include <algorithm>
- using namespace std;
- int n,m;
- const int N = 1e5+10, M = 2e6 + 10;
- typedef long long ll;
- ll g[2][M];
-
- int main(){
- ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- cin >> n >> m;
- for (int i = 1; i <= n; i ++){
- int x;cin>>x;
- if (x<0) g[0][-x]=1;
- if (x>=0) g[1][x]=1;
- }
-
- // 前缀和 往左右走i步有多少个宝石
- for (int i = 1; i <= m; i ++) {
- g[0][i]+=g[0][i-1];
- g[1][i]+=g[1][i-1];
- }
-
- ll Lres = 0;
- for (int i = 1; i <= m; i ++){
- ll dis = g[0][i];
- if (m-2*i>0) dis += g[1][m - 2*i];
- Lres = max(Lres,dis);
- }
-
- ll Rres = 0;
- for (int i = 1; i <= m; i ++){
- ll dis = g[1][i];
- if (m-2*i>0) dis += g[0][m - 2*i];
- Rres = max(Rres,dis);
- }
-
- cout << max(Lres,Rres) << "\n";
-
- return 0;
- }
题意:给一个字符串,可以在开头处加入任意数目个指定字符l/q/b,问能否将字符串转化为一个回文字符串。
思路:基础题实锤。既然能在字符串前面加入任意数目个l/q/b,那字符串最后面的l/q/b完全可以忽略,因为一定可以配对,问题转化为:删掉给定字符串最后面的l/q/b字符,问剩下的字符是否是回文串,评测一共是10组数据,一组字符串长度最大是1e6,也就是1e7的复杂度,On可以随便写,同时从两边遍历中间,发现对应不上就直接停止。
复杂度:O(N)
- #include <iostream>
- #include <algorithm>
- using namespace std;
- void solve(){
- string s; cin >> s;
- int pos = s.length()-1;
- for (; pos; pos --){
- if (s[pos]!='l' && s[pos] !='q' && s[pos]!='b') break;
- }
- string tmp = s.substr(0, pos+1); // 去掉后面那一串前面可以任意堆叠的
- bool fg = true;
- for (int i = 0,j = tmp.length()-1; i <= j; i ++, j --){ // 看看剩下的是不是回文串就可以了 复杂度On 1E7数据1秒随便跑
- if (tmp[i] != tmp[j]){
- fg = false;
- break;
- }
- }
- cout << (fg ? "Yes" : "No") << "\n";
- }
-
- int main(){
- ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- int t; cin >> t;
- while (t--){
- solve();
- }
- return 0;
- }
以上仅为oj平台的测评结果( 评测OJ:首页 - DashOJ),若有最优解或者Hack数据欢迎在评论区讨论;
最后的最后,祝大家五一假期快乐!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。