template <typename T>inline void read(T& t){ T sum = 0, f = 1; char c=getchar(); while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();} while(isdigit(c)) sum=sum*10+c-'0',c=getchar(); t=sum*f; } template<typename T> inline void write(T x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); }
下述数位是从右往左0开始计算
取出整数n在二进制表示下的第k位 (n>>k)&1 取出整数n在二进制表示下的第0~k-1位(后k位) n&((1<<k)-1) 把整数n在二进制表示下的第k位取反 n^(1<<k) 对整数n在二进制表示下的第k位赋值1 n|(1<<k) 对整数n在二进制表示下的第k位赋值0 n&(~(1<<k)) n|(0<<k)
针对于该题,我们想到得到的结果是不超过m的最大数字所以我们可以通过二进制枚举,由于题目中数据m为<=10e9,所以二进制最多又30位,那么我们从最高位开始枚举,在不超过m的前提下,尽可能让高位的1更多,这样便能够起到最后数字越大的效果。,枚举每一位的时候,当该位填1的时候<=m才会有选择到底填0还是1,由于需要经过n次运算,使得最后的数越大,那么我们需要在当前位填1<=m的前提下,再去判断0,1经过n次运算得到的数哪个更大,再去选择填0还是1,倘若当前位填1大于m,那么当前位只能填0.
/* 21 世纪,许多人得了一种奇怪的病:起床困难综合症,其临床表现为:起床难,起床后精神不佳。 作为一名青春阳光好少年,atm 一直坚持与起床困难综合症作斗争。 通过研究相关文献,他找到了该病的发病原因: 在深邃的太平洋海底中,出现了一条名为 drd 的巨龙,它掌握着睡眠之精髓,能随意延长大家的睡眠时间。 正是由于 drd 的活动,起床困难综合症愈演愈烈, 以惊人的速度在世界上传播。 为了彻底消灭这种病,atm 决定前往海底,消灭这条恶龙。 历经千辛万苦,atm 终于来到了 drd 所在的地方,准备与其展开艰苦卓绝的战斗。 drd 有着十分特殊的技能,他的防御战线能够使用一定的运算来改变他受到的伤害。 具体说来,drd 的防御战线由 n 扇防御门组成。 每扇防御门包括一个运算 op 和一个参数 t,其中运算一定是 OR,XOR,AND 中的一种,参数则一定为非负整数。 如果还未通过防御门时攻击力为 x,则其通过这扇防御门后攻击力将变为 x op t。 最终 drd 受到的伤害为对方初始攻击力 x 依次经过所有 n 扇防御门后转变得到的攻击力。 由于 atm 水平有限,他的初始攻击力只能为 0 到 m 之间的一个整数(即他的初始攻击力只能在 0,1,…,m 中任选,但在通过防御门之后的攻击力不受 m 的限制)。 为了节省体力,他希望通过选择合适的初始攻击力使得他的攻击能让 drd 受到最大的伤害,请你帮他计算一下,他的一次攻击最多能使 drd 受到多少伤害。 输入格式 第 1 行包含 2 个整数,依次为 n,m,表示 drd 有 n 扇防御门,atm 的初始攻击力为 0 到 m 之间的整数。 接下来 n 行,依次表示每一扇防御门。每行包括一个字符串 op 和一个非负整数 t,两者由一个空格隔开,且 op 在前,t 在后,op 表示该防御门所对应的操作,t 表示对应的参数。 输出格式 输出一个整数,表示 atm 的一次攻击最多使 drd 受到多少伤害。 输入样例: 3 10 AND 5 OR 6 XOR 7 输出样例: 1 样例解释 atm可以选择的初始攻击力为 0,1,…,10。 假设初始攻击力为 4,最终攻击力经过了如下计算 4 AND 5 = 4 4 OR 6 = 6 6 XOR 7 = 1 类似的,我们可以计算出初始攻击力为 1,3,5,7,9 时最终攻击力为 0,初始攻击力为 0,2,4,6,8,10 时最终攻击力为 1,因此 atm 的一次攻击最多使 drd 受到的伤害值为 1。 运算解释 在本题中,选手需要先将数字变换为二进制后再进行计算。如果操作的两个数二进制长度不同,则在前补 0 至相同长度。 OR 为按位或运算,处理两个长度相同的二进制数,两个相应的二进制位中只要有一个为 1,则该位的结果值为 1,否则为 0。 XOR 为按位异或运算,对等长二进制模式或二进制数的每一位执行逻辑异或操作。如果两个相应的二进制位不同(相异),则该位的结果值为 1,否则该位为 0。 AND 为按位与运算,处理两个长度相同的二进制数,两个相应的二进制位都为 1,该位的结果值才为 1,否则为 0。 例如,我们将十进制数 5 与十进制数 3 分别进行 OR、XOR 与 AND 运算,可以得到如下结果: 0101 (十进制 5) 0101 (十进制 5) 0101 (十进制 5) OR 0011 (十进制 3) XOR 0011 (十进制 3) AND 0011 (十进制 3) = 0111 (十进制 7) = 0110 (十进制 6) = 0001 (十进制 1) */ #include <iostream> #include <algorithm> using namespace std; const int N=100010; int a[N], op[N]; char str[4]; int n,m; bool cal(bool x, int j); int main() { cin>>n>>m; for(int i=0;i<n;++i) { cin>>str>>a[i]; if(str[0]=='O') op[i]=1; else if(str[0]=='X') op[i]=2; else op[i]=3; } int ans=0; for(int i=29;i>=0;--i)//从高位到低位枚举填0还是1 { if((1<<i)<=m) { bool x=cal(0, i), y=cal(1, i); if(x>=y) ans |= (x<<i); else { ans |= (y<<i); m -= 1<<i; } } else ans |= cal(0,i)<<i; } cout<<ans<<endl; return 0; } bool cal(bool x, int j) { for(int i=0;i<n;++i) { if(op[i]==1) x |= ((a[i]>>j)&1); else if(op[i]==2) x ^= ((a[i]>>j)&1); else if(op[i]==3) x &= ((a[i]>>j)&1); } return x; }
(原码反码补码)
/* 原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. [+1]原 = 0000 0001 [-1]原 = 1000 0001 反码的表示方法是: 正数的反码是其本身 负数的反码是在其原码的基础上, 符号位不变,其余各个位取反. [+1] = [00000001]原 = [00000001]反 [-1] = [10000001]原 = [11111110]反 补码的表示方法是: 正数的补码就是其本身 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1) [+1] = [00000001]原 = [00000001]反 = [00000001]补 [-1] = [10000001]原 = [11111110]反 = [11111111]补 */
/* 求 a 的 b 次方对 p 取模的值。 输入格式 三个整数 a,b,p ,在同一行用空格隔开。 输出格式 输出一个整数,表示a^b mod p的值。 数据范围 0≤a,b≤109 1≤p≤109 输入样例: 3 2 7 输出样例: */ #include <iostream>using namespace std; typedef long long LL; int main( { LL a,b,p; cin>>a>>b>>p; LL res=1%p; for(;b;b>>=1) { if(b&1) res = (res*a)%p; a = (a*a)%p; } cout<<res<<endl; return 0; }
/* 求 a 乘 b 对 p 取模的值。 输入格式 第一行输入整数a,第二行输入整数b,第三行输入整数p。 输出格式 输出一个整数,表示a*b mod p的值。 数据范围 1≤a,b,p≤1018 输入样例: 3 4 5 输出样例: 2 */ #include <iostream> using namespace std; typedef long long LL; LL a, b, p; int main () { //scanf("%lld\n%lld\n%lld",&a,&b,&p); cin>>a>>b>>p; LL res=0; while(b) { if(b&1) res = (res + a) % p; a = a * 2 % p; b>>=1; } cout<<res<<endl; return 0; }
通过二进制下每一位的0/1表示该位置有没有被选择,在进行递归枚举的时候当枚举完n个位置之后对n个位置进行一次遍历若该位置上为1则输出对应的位置编号,此为递归截止条件,每一次递归有两种选择即在判断每一个位置的时候有两种选择:选与不选。选的话需要将对应位置上的数字改为1,不选择的话则不需要做变化。
/* 从 1∼n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。 输入格式 输入一个整数 n。 输出格式 每行输出一种方案。 同一行内的数必须升序排列,相邻两个数用恰好 1 个空格隔开。 对于没有选任何数的方案,输出空行。 本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。 数据范围 1≤n≤15 输入样例: 3 输出样例: 3 2 2 3 1 1 3 1 2 1 2 3 */ #include <iostream> using namespace std; int n; void dfs(int u, int state); int main() { cin>>n; dfs(0,0); return 0; } void dfs(int u, int state) { if(u==n) { for(int i=0;i<n;++i) if(state>>i&1) cout<<i+1<<" "; cout<<endl; return ; } dfs(u+1, state); dfs(u+1, state|(1<<u)); }
在递归实现指数型枚举的基础上加上了只能选m个的限制
/* 从 1∼n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。 输入格式 两个整数 n,m ,在同一行用空格隔开。 输出格式 按照从小到大的顺序输出所有方案,每行 1 个。 首先,同一行内的数升序排列,相邻两个数用一个空格隔开。 其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如 1 3 5 7 排在 1 3 6 8 前面)。 数据范围 n>0 , 0≤m≤n , n+(n−m)≤25 输入样例: 5 3 输出样例: 1 2 3 1 2 4 1 2 5 1 3 4 1 3 5 1 4 5 2 3 4 2 3 5 2 4 5 3 4 5 思考题:如果要求使用非递归方法,该怎么做呢? */ #include <iostream> using namespace std; int n, m; void dfs(int u, int num, int state); int main() { cin>>n>>m; dfs(0, 0, 0); return 0; } void dfs(int u, int num, int state) { if(num+n-u<m||num>m) return ; if(num==m) { for(int i=0;i<n;++i) if(state>>i&1) cout<<i+1<<" "; cout<<endl; return ; } dfs(u+1, num+1, state|(1<<u)); dfs(u+1, num, state); } #include <iostream> #include <stack> using namespace std; int n, m; struct State { int pos,u,num,state; }; int main() { cin>>n>>m; stack<State> stk; stk.push({0,0,0,0}); while(stk.size()) { auto t = stk.top(); stk.pop(); if(t.pos == 0) { if(t.num+n-t.u<m || t.num>m) continue; if(t.num == m) { for(int i=0;i<n;++i) if(t.state>>i&1) cout<<i+1<<" "; cout<<endl; continue; } t.pos=1; stk.push(t); stk.push({0,t.u+1,t.num+1,t.state|(1<<t.u)}); } else if(t.pos == 1) { t.pos=2; stk.push(t); stk.push({0,t.u+1,t.num,t.state}); } else continue; } return 0; }
/* 把 1∼n 这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。 输入格式 一个整数 n。 输出格式 按照从小到大的顺序输出所有方案,每行 1 个。 首先,同一行相邻两个数用一个空格隔开。 其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。 数据范围 1≤n≤9 输入样例: 3 输出样例: 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 */ #include <iostream> #include <vector> using namespace std; const int N=15; bool st[N]; vector<int> p; int n; void dfs(int u); int main() { cin>>n; dfs(0); return 0; } void dfs(int u) { if(u==n) { for(auto x:p) cout<<x<<' '; cout<<endl; } for(int i=1;i<=n;++i) { if(!st[i]) { st[i]=true; p.push_back(i); dfs(u+1); p.pop_back(); st[i]=false; } } }
/* 输入一组数字(可能包含重复数字),输出其所有的排列方式。 样例 输入:[1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] */ class Solution { public: vector<vector<int>> res; vector<int> path; vector<vector<int>> permutation(vector<int>& nums) { sort(nums.begin(),nums.end());//保证相同的数字连续 path.resize(nums.size()); dfs(nums, 0, 0, 0); return res; } void dfs(vector<int> &nums, int u, int start, int state) //u表示已经放的个数,start表示从哪一位开始放,state表示已经放过的状态 { if(u == nums.size()) { res.push_back(path); return ; } if(!u || nums[u] != nums[u-1]) start=0; for(int i=start;i<nums.size();++i)//从start开始找没有被放过的元素 { if(!(state>>i & 1)) { path[i]=nums[u]; dfs(nums,u+1,i+1,state|(1<<i)); } } } };
/* 7254是一个不寻常的数,因为它可以表示为7254 = 39 x 186,这个式子中1~9每个数字正好出现一次 输出所有这样的不同的式子(乘数交换被认为是相同的式子) 结果小的先输出;结果相同的,较小的乘数较小的先输出。 输入描述: 每一行输出一个式子,式子中的等号前后空格、乘号(用字母x代表)前后空格 较小的乘数写在前面 */ #include <iostream> #include <algorithm> #include <vector> using namespace std; int a[9]={1,2,3,4,5,6,7,8,9}; vector<int> res; struct node { int a,b,c; bool operator < (node& A) { if(a!=A.a) return a<A.a; else return b<A.b; } }; node ans[110]; int idx; bool st[9]; int cal(vector<int> a, int i, int j); void dfs(int u); int main() { dfs(0); sort(ans,ans+idx); for(int i=0;i<idx;++i) { bool flag=true; for(int j=i+1;j<idx;++j) { if(ans[i].b==ans[j].c) { flag=false; cout << ans[i].a << " " << "=" << " " << ans[i].b << " " << "x" << " " << ans[i].c << endl; break; } if(ans[i].a!=ans[j].a) break; } //if(flag) } return 0; } int cal(vector<int> a, int i, int j) { int t=0; for(int k=j;k>=i;--k) t=t*10+a[k]; return t; } void dfs(int u) { if(u==9) { for (int i = 3; i < 7; ++i) for (int j = i + 1; j < 8; ++j) if (cal(res, 0, i) == cal(res, i + 1, j) * cal(res, j + 1, 8)) { ans[idx].a = cal(res, 0, i); ans[idx].b = cal(res, i + 1, j); ans[idx].c = cal(res, j + 1, 8); ++idx; } return ; } for(int i=0;i<9;++i) { if(!st[i]) { st[i] = true; res.push_back(a[i]); dfs(u + 1); st[i] = false; res.pop_back(); } } }
/* 给定你一个长度为 n 的整数数列。 请你使用快速排序对这个数列按照从小到大进行排序。 并将排好序的数列按顺序输出。 输入格式 输入共两行,第一行包含整数 n。 第二行包含 n 个整数(所有整数均在 1∼109 范围内),表示整个数列。 输出格式 输出共一行,包含 n 个整数,表示排好序的数列。 数据范围 1≤n≤100000 输入样例: 5 3 1 2 4 5 输出样例: 1 2 3 4 5 */ #include <iostream> #include <algorithm> using namespace std; const int N=1e6+10; int q[N]; int n; void quick_sort(int q[],int l,int r); int main() { scanf("%d",&n); for(int i=0;i<n;++i) scanf("%d",&q[i]); quick_sort(q,0,n-1); for(int i=0;i<n;++i) printf("%d ",q[i]); return 0; } void quick_sort(int q[],int l,int r) { if(l>=r) return ; int x=q[l+r>>1],i=l-1,j=r+1; while(i<j) { do ++i; while(q[i]<x); do --j; while(q[j]>x); if(i<j) swap(q[i],q[j]); } quick_sort(q,l,j); quick_sort(q,j+1,r); }
/* 给定一个长度为 n 的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k 个数。 输入格式 第一行包含两个整数 n 和 k。 第二行包含 n 个整数(所有整数均在 1∼109 范围内),表示整数数列。 输出格式 输出一个整数,表示数列的第 k 小数。 数据范围 1≤n≤100000, 1≤k≤n 输入样例: 5 3 2 4 1 5 3 输出样例: 3 */ #include <iostream> #include <cstdio> using namespace std; const int N=1e6+10; int q[N]; int n,k; void quick_sort(int p[],int l,int r); int main() { scanf("%d %d",&n,&k); for(int i=0;i<n;++i) { scanf("%d",&q[i]); } quick_sort(q,0,n-1); printf("%d",q[k-1]); return 0; } void quick_sort(int p[],int l,int r) { if(l>=r) return ; int x=p[l+r>>1],i=l-1,j=r+1; while(i<j) { do ++i; while(p[i]<x); do --j; while(p[j]>x); if(i<j) { swap(p[i],p[j]); } } quick_sort(p,l,j); quick_sort(p,j+1,r); }
/* 给定你一个长度为 n 的整数数列。 请你使用归并排序对这个数列按照从小到大进行排序。 并将排好序的数列按顺序输出。 输入格式 输入共两行,第一行包含整数 n。 第二行包含 n 个整数(所有整数均在 1∼109 范围内),表示整个数列。 输出格式 输出共一行,包含 n 个整数,表示排好序的数列。 数据范围 1≤n≤100000 输入样例: 5 3 1 2 4 5 输出样例: 1 2 3 4 5 */ #include <iostream> #include <algorithm> using namespace std; const int N=1e6+10; int q[N],tmp[N]; int n; void merge_sort(int q[],int l,int r); int main() { scanf("%d",&n); for(int i=0;i<n;++i) scanf("%d",&q[i]); merge_sort(q,0,n-1); for(int i=0;i<n;++i) printf("%d ",q[i]); return 0; } void merge_sort(int q[],int l,int r) { if(l>=r) return ; int mid=(l+r)>>1; merge_sort(q,l,mid); merge_sort(q,mid+1,r); int k=0,i=l,j=mid+1; while(i<=mid&&j<=r) { if(q[i]<=q[j]) tmp[k++]=q[i++]; else tmp[k++]=q[j++]; } while(i<=mid) tmp[k++]=q[i++]; while(j<=r) tmp[k++]=q[j++]; for(i=l,j=0;i<=r,j<k;++i,++j) q[i]=tmp[j]; }
/* 给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。 逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆序对;否则不是。 输入格式 第一行包含整数 n,表示数列的长度。 第二行包含 n 个整数,表示整个数列。 输出格式 输出一个整数,表示逆序对的个数。 数据范围 1≤n≤100000, 数列中的元素的取值范围 [1,109]。 输入样例: 6 2 3 4 5 6 1 输出样例: 5 */ #include <iostream> #include <algorithm> using namespace std; const int N=1e6+10; typedef long long LL; int q[N],tmp[N]; int n; LL merge_sort(int l,int r); int main() { scanf("%d",&n); for(int i=0;i<n;++i) scanf("%d",&q[i]); cout<<merge_sort(0,n-1)<<endl; return 0; } LL merge_sort(int l,int r) { if(l>=r) return 0; int mid=l+r >>1; LL res=merge_sort(l,mid)+merge_sort(mid+1,r); int k=0,i=l,j=mid+1; while(i<=mid&&j<=r) { if(q[i]<=q[j]) tmp[k++]=q[i++]; else { tmp[k++]=q[j++]; res+=(mid-i+1); } } while(i<=mid) tmp[k++]=q[i++]; while(j<=r) tmp[k++]=q[j++]; for(i=l,j=0;i<=r,j<k;++i,++j) q[i]=tmp[j]; return res; }
#include <iostream> #include <algorithm> #include <cstring> using namespace std; const int N=110; int m[4]={6,3,2,1}; int n; int a[N]; void shell(int q[], int len); int main() { cin>>n; for(int i=0;i<n;++i) cin>>a[i]; shell(a, n); for(int i=0;i<n;++i) cout<<a[i]<<" "; return 0; } void shell(int q[], int len) { for(int i=0;i<4;++i) { for(int j=m[i];j<len;++j) { int t=q[j]; int k=j-m[i]; while(k>=0&&q[k]>t) { q[k+m[i]]=q[k]; k-=m[i]; } q[k+m[i]]=t; } for(int i=0;i<len;++i) cout<<q[i]<<' '; cout<<endl; } }
二分的应用在数据结构单调队列章节也有一道例题
\[\begin{align} &二分的本质不是单调性(有单调性一定可以二分) \\ &①.整数二分 \\ &二分的本质是边界,给定一个区间,我们在区间上定义一种性质,使得该性质右半边满足,左半边不满足(整数二分左右两边没有交点) \\ &那么我们既可以二分出左右两个边界点,如下图所示 \\ & \\ &②.浮点数二分 \\ &由于浮点数二分不会发生整除,所以与整数二分相似,不需要考虑发生整除的情况 \end{align} \]整数二分
找最小的最大值:l=mid, r=mid-1;
找最大的最小值:r=mid, l=mid+1;
/* 给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。 对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。 如果数组中不存在该元素,则返回 -1 -1。 输入格式 第一行包含整数 n 和 q,表示数组长度和询问个数。 第二行包含 n 个整数(均在 1∼10000 范围内),表示完整数组。 接下来 q 行,每行包含一个整数 k,表示一个询问元素。 输出格式 共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。 如果数组中不存在该元素,则返回 -1 -1。 数据范围 1≤n≤100000 1≤q≤10000 1≤k≤10000 输入样例: 6 3 1 2 2 3 3 4 3 4 5 输出样例: 3 4 5 5 -1 -1 */ #include <iostream> using namespace std; const int N=1e5+10; int q[N]; int n,T; int main() { cin>>n>>T; for(int i=0;i<n;++i) cin>>q[i]; while(T--) { int x; cin>>x; int l=0,r=n-1; while(l<r) { int mid=l+r>>1; if(q[mid]>=x) r=mid; else l=mid+1; } if(q[l]!=x) cout<<"-1 -1"<<endl; else { cout<<l<<" "; l=0,r=n-1; while(l<r) { int mid=l+r+1>>1; if(q[mid]<=x) l=mid; else r=mid-1; } cout<<r<<endl; } } return 0; }
/* 峰值定义为比左右相邻元素大的元素。 给定一个长度为 n 的数组 nums,数组下标从0开始,保证 nums[i]≠nums[i+1],请找出该数组的峰值,并返回峰值的下标。 数组中可能包含多个峰值,只需返回任意一个即可。 假定 nums[-1] = nums[n] = -∞。 本题中数组是隐藏的,你可以通过我们预设的 int 函数 query 来获得数组中某个位置的数值是多少。 例如,query(a) 即可获得下标为 a 的元素的值。 注意: query 函数的调用次数不能超过 min(3×⌈log2n⌉,10)。 数据范围 1≤n≤105, 数组中的整数在 int 范围内。 输入样例1: [1, 2, 3, 1] 输出样例1: 2 输入样例2: [1, 2, 1, 3, 5, 6, 4] 输出样例2: 1 样例解释 对于样例 1,3 是峰值,其下标为 2。 对于样例 2,2 和 6 是峰值,下标为 1 和 5,输出任意一个均可。 */ // Forward declaration of queryAPI. // int query(int x); // return int means nums[x]. class Solution { public: int findPeakElement(int n) { int l=0,r=n-1; while(l<r) { int mid=l+r>>1; if(query(mid)>query(mid+1)) r=mid; else l=mid+1; } return l; } };
/* 给定一个浮点数 n,求它的三次方根。 输入格式 共一行,包含一个浮点数 n。 输出格式 共一行,包含一个浮点数,表示问题的解。 注意,结果保留 6 位小数。 数据范围 −10000≤n≤10000 输入样例: 1000.00 输出样例: 10.000000 */ #include <iostream> using namespace std; const double eps=1e-8; int main() { double n; scanf("%lf",&n); double l=-10000,r=10000; while(r-l>eps) { double mid=(l+r)/2; if(mid*mid*mid>=n) r=mid; else l=mid; } printf("%.6f",l); return 0; }
在高精度中对于大整数的存储方式都是一致的,可以用数组或者vector进行存储
/* 给定两个正整数(不含前导 0),计算它们的和。 输入格式 共两行,每行包含一个整数。 输出格式 共一行,包含所求的和。 数据范围 1≤整数长度≤100000 输入样例: 12 23 输出样例: 35 */ //STL写法 #include <iostream> #include <algorithm> #include <cstring> using namespace std; const int N=1e6+10; char num1[N],num2[N]; vector<int> a,b; vector<int> add(vector<int> A, vector<int> B); int main() { cin>>num1>>num2; int len1=strlen(num1),len2=strlen(num2); for(int i=0;i<len1;++i) a.push_back(num1[len1-1-i]-'0'); for(int i=0;i<len2;++i) b.push_back(num2[len2-1-i]-'0'); auto c=add(a,b); for(int i=c.size()-1;i>=0;--i) cout<<c[i]; return 0; } vector<int> add(vector<int> A,vector<int> B) { if(A.size()<B.size()) return add(B,A); vector<int> c; int t=0; for(int i=0;i<A.size();++i) { t+=A[i]; if(i<B.size()) t+=B[i]; c.push_back(t%10); t/=10; } if(t) c.push_back(t); return c; } //数组写法 #include <iostream> #include <algorithm> #include <cstring> using namespace std; const int N = 1e6+10; char num1[N], num2[N];//存放两个很大的整数,这里是以字符串的形式存储的 int n1[N], n2[N];//存放两个很大的整数字符串变成数字之后的值 int res[N] = { 0 };//存放结果 int main() { cin >> num1 >> num2; int l1 = strlen(num1); int l2 = strlen(num2); for (int i = 0; i < l1; ++i) { n1[i] = (num1[l1 - 1 - i] - '0');//将字符转换成数字 } for (int i = 0; i < l2; ++i) { n2[i] = (num2[l2 - 1 - i] - '0');//将字符转换成数字 } int over = 0;//用于进位 for (int i = 0; i < N; ++i)//进行加法运算 { int temp = n1[i] + n2[i] + over; res[i] = temp % 10; over = temp / 10; } int j=N-1; for (j; j >= 0; --j)//去前置0 { if (res[j]) { break; } } for (int k = j; k>=0; --k)//倒叙输出结果 { cout << res[k]; } return 0; }
/* 给定两个正整数(不含前导 0),计算它们的差,计算结果可能为负数。 输入格式 共两行,每行包含一个整数。 输出格式 共一行,包含所求的差。 数据范围 1≤整数长度≤105 输入样例: 32 11 输出样例: 21 */ #include <iostream> #include <algorithm> #include <cstring> #include <vector> using namespace std; const int N=1e6+10; char num1[N],num2[N]; vector<int> a,b; bool cmp(vector<int> A, vector<int> B);//判断是否A>=B vector<int> sub(vector<int> A, vector<int> B); int main() { cin>>num1>>num2; int len1=strlen(num1),len2=strlen(num2); for(int i=0;i<len1;++i) a.push_back(num1[len1-i-1]-'0'); for(int i=0;i<len2;++i) b.push_back(num2[len2-i-1]-'0'); if(cmp(a,b)) { auto c=sub(a,b); for(int i=c.size()-1;i>=0;--i) cout<<c[i]; } else { auto c=sub(b,a); printf("-"); for(int i=c.size()-1;i>=0;--i) cout<<c[i]; } return 0; } bool cmp(vector<int> A, vector<int> B) { if(A.size()!=B.size()) return A.size()>B.size(); for(int i=A.size()-1;i>=0;--i) if(A[i]!=B[i]) return A[i]>B[i]; return true; } vector<int> sub(vector<int> A, vector<int> B) { vector<int> c; for(int i=0,t=0;i<A.size();++i) { t=A[i]-t; if(i<B.size()) t-=B[i]; c.push_back((t+10)%10); if(t<0) t=1; else t=0; } while(c.size()>1&&c.back()==0) c.pop_back(); return c; }
/* 给定两个非负整数(不含前导 0) A 和 B,请你计算 A×B 的值。 输入格式 共两行,第一行包含整数 A,第二行包含整数 B。 输出格式 共一行,包含 A×B 的值。 数据范围 1≤A的长度≤100000, 0≤B≤10000 输入样例: 2 3 输出样例: 6 */ #include <iostream> #include <algorithm> #include <vector> using namespace std; vector<int> mul(vector<int> A,int B); int main() { string a; int b; cin>>a>>b; vector<int> A; for(int i=0;i<a.size();++i) A.push_back(a[a.size()-i-1]-'0'); auto c=mul(A,b); for(int i=c.size()-1;i>=0;--i) cout<<c[i]; return 0; } vector<int> mul(vector<int> A,int B) { vector<int> c; int t=0; for(int i=0;i<A.size()||t;++i) { if(i<A.size()) t+=(A[i]*B); c.push_back(t%10); t/=10; } while(c.size()>1&&c.back()==0) c.pop_back(); return c; }
/* 给定两个非负整数(不含前导 0) A,B,请你计算 A/B 的商和余数。 输入格式 共两行,第一行包含整数 A,第二行包含整数 B。 输出格式 共两行,第一行输出所求的商,第二行输出所求余数。 数据范围 1≤A的长度≤100000, 1≤B≤10000, B 一定不为 0 输入样例: 7 2 输出样例: 3 1 */ #include <iostream> #include <algorithm> #include <vector> using namespace std; vector<int> div(vector<int> A, int B, int &r); int main() { string a; int b; cin>>a>>b; vector<int> A; for(int i=0;i<a.size();++i) A.push_back(a[a.size()-i-1]-'0'); int r=0; auto c=div(A,b,r); for(int i=c.size()-1;i>=0;--i) cout<<c[i]; cout<<endl<<r<<endl; return 0; } vector<int> div(vector<int> A,int B,int &r) { vector<int> c; for(int i=A.size()-1;i>=0;--i) { r=r*10+A[i]; c.push_back(r/B); r%=B; } reverse(c.begin(),c.end()); while(c.size()>1&&c.back()==0) c.pop_back(); return c; }
/* 输入一个长度为 n 的整数序列。 接下来再输入 m 个询问,每个询问输入一对 l,r。 对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。 输入格式 第一行包含两个整数 n 和 m。 第二行包含 n 个整数,表示整数数列。 接下来 m 行,每行包含两个整数 l 和 r,表示一个询问的区间范围。 输出格式 共 m 行,每行输出一个询问的结果。 数据范围 1≤l≤r≤n, 1≤n,m≤100000, −1000≤数列中元素的值≤1000 输入样例: 5 3 2 1 3 6 4 1 2 1 3 2 4 输出样例: 3 6 10 */ #include <iostream> #include <algorithm> using namespace std; const int N=1e5+10; int pre[N],a[N]; int n,m; int main() { cin>>n>>m; for(int i=1;i<=n;++i) cin>>a[i]; for(int i=1;i<=n;++i) pre[i]=pre[i-1]+a[i]; for(int i=0;i<m;++i) { int l,r; cin>>l>>r; cout<<pre[r]-pre[l-1]<<endl; } return 0; }\[\begin{align} &当我们将前缀和推广矩阵当中求解子矩阵的前缀和,即二维前缀和. \\ &见下图: \end{align} \]
/* 输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。 对于每个询问输出子矩阵中所有数的和。 输入格式 第一行包含三个整数 n,m,q。 接下来 n 行,每行包含 m 个整数,表示整数矩阵。 接下来 q 行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。 输出格式 共 q 行,每行输出一个询问的结果。 数据范围 1≤n,m≤1000, 1≤q≤200000, 1≤x1≤x2≤n, 1≤y1≤y2≤m, −1000≤矩阵内元素的值≤1000 输入样例: 3 4 3 1 7 2 4 3 6 2 8 2 1 2 3 1 1 2 2 2 1 3 4 1 3 3 4 输出样例: 17 27 21 */ #include <iostream> #include <algorithm> using namespace std; const int N=1010; int a[N][N],pre[N][N]; int n,m,q; int main() { cin>>n>>m>>q; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) cin>>a[i][j]; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+a[i][j]; for(int i=0;i<q;++i) { int x1,y1,x2,y2; cin>>x1>>y1>>x2>>y2; cout<<pre[x2][y2]-pre[x1-1][y2]-pre[x2][y1-1]+pre[x1-1][y1-1]<<endl; } return 0; }
/* 输入一个长度为 n 的整数序列。 接下来输入 m 个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r] 之间的每个数加上 c。 请你输出进行完所有操作后的序列。 输入格式 第一行包含两个整数 n 和 m。 第二行包含 n 个整数,表示整数序列。 接下来 m 行,每行包含三个整数 l,r,c,表示一个操作。 输出格式 共一行,包含 n 个整数,表示最终序列。 数据范围 1≤n,m≤100000, 1≤l≤r≤n, −1000≤c≤1000, −1000≤整数序列中元素的值≤1000 输入样例: 6 3 1 2 2 1 2 1 1 3 1 3 5 1 1 6 1 输出样例: 3 4 5 3 4 2 */ #include <iostream> using namespace std; const int N=1e5+10; int a[N],b[N]; int n,m; void insert(int l,int r,int c); int main() { cin>>n>>m; for(int i=1;i<=n;++i) cin>>a[i]; for(int i=1;i<=n;++i) insert(i,i,a[i]); while(m--) { int l,r,c; cin>>l>>r>>c; insert(l,r,c); } for(int i=1;i<=n;++i) { b[i]+=b[i-1]; } for(int i=1;i<=n;++i) cout<<b[i]<<" "; return 0; } void insert(int l,int r,int c) { b[l]+=c; b[r+1]-=c; }\[\begin{align} &差分矩阵与一维差分相类似,我们不需要去考虑如何构造,采用与一维数组一致的做法即可. \\ \end{align} \]
/* 输入一个 n 行 m 列的整数矩阵,再输入 q 个操作,每个操作包含五个整数 x1,y1,x2,y2,c,其中 (x1,y1) 和 (x2,y2) 表示一个子矩阵的左上角坐标和右下角坐标。 每个操作都要将选中的子矩阵中的每个元素的值加上 c。 请你将进行完所有操作后的矩阵输出。 输入格式 第一行包含整数 n,m,q。 接下来 n 行,每行包含 m 个整数,表示整数矩阵。 接下来 q 行,每行包含 5 个整数 x1,y1,x2,y2,c,表示一个操作。 输出格式 共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。 数据范围 1≤n,m≤1000, 1≤q≤100000, 1≤x1≤x2≤n, 1≤y1≤y2≤m, −1000≤c≤1000, −1000≤矩阵内元素的值≤1000 输入样例: 3 4 3 1 2 2 1 3 2 2 1 1 1 1 1 1 1 2 2 1 1 3 2 3 2 3 1 3 4 1 输出样例: 2 3 4 1 4 3 4 1 2 2 2 2 */ #include <iostream> using namespace std; const int N=1010; int a[N][N],b[N][N]; int n,m,q; void insert(int x1,int y1,int x2,int y2,int c); int main() { cin>>n>>m>>q; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) cin>>a[i][j]; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) insert(i,j,i,j,a[i][j]); while(q--) { int x1,y1,x2,y2,c; cin>>x1>>y1>>x2>>y2>>c; insert(x1,y1,x2,y2,c); } for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1]; } } for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { cout<<b[i][j]<<" "; } puts(""); } return 0; } void insert(int x1,int y1,int x2,int y2,int c) { b[x1][y1]+=c; b[x2+1][y1]-=c; b[x1][y2+1]-=c; b[x2+1][y2+1]+=c; }
/* 给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。 输入格式 第一行包含整数 n。 第二行包含 n 个整数(均在 0∼105 范围内),表示整数序列。 输出格式 共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。 数据范围 1≤n≤105 输入样例: 5 1 2 2 3 5 输出样例: 3 */ #include <iostream> #include <algorithm> using namespace std; const int N=1e5+10; int a[N],s[N]; int n; int main() { cin>>n; for(int i=0;i<n;++i) cin>>a[i]; int res=0; for(int i=0,j=0;i<n;++i) { ++s[a[i]]; while(s[a[i]]>1) { --s[a[j]]; ++j; } res=max(res,i-j+1); } cout<<res<<endl; return 0; }\[\begin{align} &针对于这道题中两个有序序列都是升序的数组,我们先分析起暴力解法,一个双重循环枚举两个数组中选择的每一个选法,当满足 \\ &a[i]+b[j]==x时输出i,j.那么之后再考虑对其的优化使用双指针算法,找到单调性,首先a,b数组单调上升,其次对于每个i \\ &而言找到一个对应的j,使得a[i]+b[j]>=x,并且j最小,那么可以发现当i右移时,j是往左移动的,那么找到了一个单调性 \\ &至此我们可以将其使用双指针进行优化. \end{align} \]
/* 给定两个升序排序的有序数组 A 和 B,以及一个目标值 x。 数组下标从 0 开始。 请你求出满足 A[i]+B[j]=x 的数对 (i,j)。 数据保证有唯一解。 输入格式 第一行包含三个整数 n,m,x,分别表示 A 的长度,B 的长度以及目标值 x。 第二行包含 n 个整数,表示数组 A。 第三行包含 m 个整数,表示数组 B。 输出格式 共一行,包含两个整数 i 和 j。 数据范围 数组长度不超过 105。 同一数组内元素各不相同。 1≤数组元素≤109 输入样例: 4 5 6 1 2 4 7 3 4 6 8 9 输出样例: 1 1 */ #include <iostream> #include <algorithm> using namespace std; const int N=1e5+10; int a[N],b[N]; int n,m,x; int main() { cin>>n>>m>>x; for(int i=0;i<n;++i) cin>>a[i]; for(int i=0;i<m;++i) cin>>b[i]; for(int i=0,j=m-1;i<n;++i) { while(j>0&&a[i]+b[j]>x) { --j; } if(a[i]+b[j]==x) cout<<i<<" "<<j<<endl; } return 0; }\[\begin{align} & 对于这道题,我们知道数组a倘若是数组b的子序列,那么数字a的顺序必定是数组b的原有次序,那么我们用双指针 \\ &去寻找答案,遍历a数组中每一个元素,在数组b中找到最左边与之匹配的一个,将两个匹配,重复该做法,得到一 \\ &个双指针的解,那么我们在假设存在一个解,那么我们能不能通过双指针找到解呢,由于我们刚使用双指针时说明 \\ &每次寻找匹配时找最左边的与之匹配的元素,那么我们存在的解的位置必然在双指针解的右边或在同一位置,那么 \\ &存在解必然可以替换为双指针解.我们遍历数组a,当a中元素在b中找到匹配则指针后移,b中指针后移,若找不到则 \\ &b中指针后移,最后若a中指针遍历完则说明找到全部的匹配. \end{align} \]
/* 给定一个长度为 n 的整数序列 a1,a2,…,an 以及一个长度为 m 的整数序列 b1,b2,…,bm。 请你判断 a 序列是否为 b 序列的子序列。 子序列指序列的一部分项按原有次序排列而得的序列,例如序列 {a1,a3,a5} 是序列 {a1,a2,a3,a4,a5} 的一个子序列。 输入格式 第一行包含两个整数 n,m。 第二行包含 n 个整数,表示 a1,a2,…,an。 第三行包含 m 个整数,表示 b1,b2,…,bm。 输出格式 如果 a 序列是 b 序列的子序列,输出一行 Yes。 否则,输出 No。 数据范围 1≤n≤m≤105, −109≤ai,bi≤109 输入样例: 3 5 1 3 5 1 2 3 4 5 输出样例: Yes */ #include <iostream> #include <algorithm> using namespace std; const int N=1e5+10; int a[N],b[N]; int n,m; int main() { cin>>n>>m; for(int i=0;i<n;++i) cin>>a[i]; for(int i=0;i<m;++i) cin>>b[i]; int i=0,j=0; while(i<n&&j<m) { if(a[i]==b[j]) ++i; ++j; } if(i==n) puts("Yes"); else puts("No"); return 0; }
双指针之快慢指针
/* 给定一个包含 n 个整数的数组,请你删除数组中的重复元素并将数组从小到大排序后输出。 输入格式 第一行包含一个整数 n。 第二行包含 n 个不超过 1000 的正整数。 输出格式 输出去重和排序完毕后的数组。 数据范围 1≤n≤1000 输入样例: 6 8 8 7 3 7 7 输出样例: 3 7 8 */ #include <iostream> #include <algorithm> using namespace std; const int N = 1010; int a[N], n; int main() { cin >> n; for(int i = 0; i < n; ++ i) cin >> a[i]; sort(a, a+n); int p = 0, q = 1; while(q < n) { if(a[p] != a[q]) { a[p + 1] = a[q]; ++p; } q++; } for(int i = 0; i <= p; ++ i) cout << a[i] << " "; return 0; }
/* 给定一个长度为 n 的数列,请你求出数列中每个数的二进制表示中 1 的个数。 输入格式 第一行包含整数 n。 第二行包含 n 个整数,表示整个数列。 输出格式 共一行,包含 n 个整数,其中的第 i 个数表示数列中的第 i 个数的二进制表示中 1 的个数。 数据范围 1≤n≤100000, 0≤数列中元素的值≤109 输入样例: 5 1 2 3 4 5 输出样例: 1 1 2 1 2 */ #include <iostream> #include <algorithm> using namespace std; const int N=1e5+10; int a[N]; int n; int main() { cin>>n; for(int i=0;i<n;++i) cin>>a[i]; for(int i=0;i<n;++i) { int res=0; for(int j=0;j<32;++j) if((a[i]>>j)&1) ++res; cout<<res<<" "; } return 0; } #include <iostream> #include <algorithm> using namespace std; int lowbit(int x); int n; int main() { cin>>n; while(n--) { int x; cin>>x; int res=0; while(x) { x-=lowbit(x); ++res ; } cout<<res<<" "; } return 0; } int lowbit(int x) { return x&(-x); }
/* 假定有一个无限长的数轴,数轴上每个坐标上的数都是 0。 现在,我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。 接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [l,r] 之间的所有数的和。 输入格式 第一行包含两个整数 n 和 m。 接下来 n 行,每行包含两个整数 x 和 c。 再接下来 m 行,每行包含两个整数 l 和 r。 输出格式 共 m 行,每行输出一个询问中所求的区间内数字和。 数据范围 −109≤x≤109, 1≤n,m≤105, −109≤l≤r≤109, −10000≤c≤10000 输入样例: 3 3 1 2 3 6 7 5 1 3 4 6 7 8 输出样例: 8 0 5 */ #include <iostream> #include <algorithm> #include <vector> using namespace std; const int N=300010; typedef pair<int, int> PII; vector<int> alls;//存储所有需要离散化的点 vector<PII> add, quary;//add存储在x位置上+c,quary存储查询的区间 int n,m; int a[N],s[N]; int find(int x); int main() { cin>>n>>m; for(int i=0;i<n;++i) { int x,c; cin>>x>>c; add.push_back({x,c}); alls.push_back(x); } for(int i=0;i<m;++i) { int l,r; cin>>l>>r; quary.push_back({l,r}); alls.push_back(l); alls.push_back(r); } //保序去重 sort(alls.begin(),alls.end()); alls.erase(unique(alls.begin(),alls.end()),alls.end()); for(auto item:add) { int x=find(item.first); a[x]+=item.second; } //求前缀和 for(int i=1;i <= alls.size();++i) s[i]=s[i-1]+a[i]; //处理查询 for(auto item:quary) { int l=find(item.first), r=find(item.second); cout<<s[r]-s[l-1]<<endl; } return 0; } int find(int x) { int l=0,r=alls.size(); while(l<r) { int mid= l+r >> 1; if(alls[mid]>=x) r=mid; else l=mid+1; } return r+1; } //unique的实现 /* vector<int>::iterator unique(vector<int> &a) { int j=0 for(int i=0;i<a.size();++i) if(!i || a[i]!=a[i-1]) a[j++]=a[i]; return a.begin()+j; } */
/* 给定 n 个区间 [li,ri],要求合并所有有交集的区间。 注意如果在端点处相交,也算有交集。 输出合并完成后的区间个数。 例如:[1,3] 和 [2,6] 可以合并为一个区间 [1,6]。 输入格式 第一行包含整数 n。 接下来 n 行,每行包含两个整数 l 和 r。 输出格式 共一行,包含一个整数,表示合并区间完成后的区间个数。 数据范围 1≤n≤100000, −109≤li≤ri≤109 输入样例: 5 1 2 2 4 5 6 7 8 7 9 输出样例: 3 */ #include <iostream> #include <algorithm> #include <vector> using namespace std; typedef pair<int, int> PII; vector<PII> section; int n; void merge(vector<PII> &segs); int main() { cin>>n; for(int i=0;i<n;++i) { int l,r; cin>>l>>r; section.push_back({l,r}); } merge(section); cout<<section.size()<<endl; return 0; } void merge(vector<PII> &segs) { sort(segs.begin(),segs.end()); vector<PII> res; int st=-2e9,ed=-2e9; for(auto seg:segs) { if(ed<seg.first) { if(st!=-2e9) res.push_back({st,ed}); st=seg.first; ed=seg.second; } else ed=max(ed,seg.second); } if(st!=-2e9) res.push_back({st,ed}); segs=res; }