网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
【输入格式】
输入的第一行包含一个整数 _T _,表示询问的个数。
接下来 _T _行,每行包含一组询问,其中第 _i _行包含两个整数 _li _和 ri,表示询问数列中第 _li _个数到第 _ri _个数的和。
【输出格式】
输出 _T _行,每行包含一个整数表示对应询问的答案。
【样例输入】
3
1 1
1 3
5 8
【样例输出】
1
4
8
【评测用例规模与约定】
对于 10% 的评测用例,1 ≤ _T _≤ 30, 1 ≤ _li _≤ _ri _≤ 100。 对于 20% 的评测用例,1 ≤ _T _≤ 100, 1 ≤ _li _≤ _ri _≤ 1000。对于 40% 的评测用例,1 ≤ _T _≤ 1000, 1 ≤ _li _≤ _ri _≤ 106。对于 70% 的评测用例,1 ≤ _T _≤ 10000, 1 ≤ _li _≤ _ri _≤ 109。对于 80% 的评测用例,1 ≤ _T _≤ 1000, 1 ≤ _li _≤ _ri _≤ 1012。
对于 90% 的评测用例,1 ≤ _T _≤ 10000, 1 ≤ _li _≤ _ri _≤ 1012。对于所有评测用例,1 ≤ _T _≤ 100000, 1 ≤ _li _≤ _ri _≤ 1012。
【解题思路:类比金字塔+前缀和 <= 100%】
将数列看成金字塔,转换成求第na层第ca个数到nb层cb个数中间
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
思路举例:
例题的5 8 ==> 8
[第3层第2个数] --[第4层第2个数] :(2 + 3)[第三层后面]+ 0[中间层] + (1 + 2)[第四层前面] = 8
再举个好看的例子: 4 15 ==> 21
[第3层第1个数] – [第5层第5个数] : (6)第三层后面 + 10 中间层 + 15 [第四层第五个数前面和] = 21
然后讲上述思路转换为编程思路:
分层解决问题,把内容模块化,然后逐个实现即可
- 估计层数 – (1+x)*x/ 2>=1e12 ==> x = 1500000层
- 所以1500000层可以容纳1e12个数,也可以用数组存下
- 关键公式:sum = 前缀和求ab中间层数的和 + a当前层后面几个数量 + b当前层前面几个数量
- 解决给定第n个数,求出他在第几层第几个数。
- 解决求中间层数
#include<bits/stdc++.h>
#include<iostream>
#define ll long long
using namespace std;
ll dp[1500000]; // 每一层的和
ll dpSum[1500000]; // 层的前缀和
/\*int dp[30] = {0,1,1,2,1,2,3,1,2,3,4,1,2,3,4,5,1,2,3,4,5,6};
1 1
2 1 2
3 1 2 3
4 1 2 3 4
5 1 2 3 4 5
\*/
//函数思路是找到a和b是第几层的第几个
//sum = 前缀和求ab中间层数的和 + a当前层后面几个数量 + b当前层前面几个数量
ll check(ll a,ll b){
ll temp = 1;
ll flag = 1;
while((temp+1)\*(temp)/2 < a){
temp++;
}
ll aDeep = temp,aCnt = aDeep - (((aDeep+1)\*(aDeep)/2) - a);
temp = 1;
flag = 1;
while((temp+1)\*(temp)/2 < b){
temp++;
}
ll bDeep = temp,bCnt = bDeep - (((bDeep+1)\*(bDeep)/2) - b);
ll sum = 0;
//cout << aDeep << ' ' << aCnt << ' ' << bDeep << ' ' << bCnt << endl;
if(bDeep > aDeep)
sum = dpSum[bDeep-1] - dpSum[aDeep];
if(bDeep > aDeep){
sum += (aDeep+aCnt)\*(aDeep+1-aCnt)/2;
sum += (1+bCnt)\*(bCnt+1-1)/2;
}else{
sum += (aCnt + bCnt)\*(bCnt + 1 - aCnt)/2;
}
return sum;
}
int main(){
dp[0] = 0;
dpSum[0] = 0;
//推算到 1500000层正好大约1e12次方个数
//处理1500000层的数据
for(int i = 1; i <= 1500000; i++){
dp[i] = dp[i-1] + i;
dpSum[i] = dpSum[i-1] + dp[i];
}
//输入数据
ll n;
cin >> n;
for(ll i = 0; i < n; i++){
ll a, b;
cin >> a >> b;
cout << check(a,b) << endl;
}
return 0;
}
试题 G: 异或变换[20 分]
时间限制: 1.0s 内存限制: 256.0MB
【问题描述】
小蓝有一个 01 串 s = s1 s2 s3 · · · sn。
以后每个时刻,小蓝要对这个 01 串进行一次变换。每次变换的规则相同。对于 01 串 s = s1 s2 s3 · · · sn,变换后的 01 串 s′ = s′1 s′2 s′3 · · · s′_n _为:
s′1 = s1;
s′_i _= si−1 ⊕ si 。
其中 _a _⊕ _b _表示两个二进制的异或,当 _a _和 _b _相同时结果为 0,当 _a _和 b
不同时结果为 1。
请问,经过 _t _次变换后的 01 串是什么?
【输入格式】
输入的第一行包含两个整数 n, t,分别表示 01 串的长度和变换的次数。第二行包含一个长度为 _n _的 01 串。
【输出格式】
输出一行包含一个 01 串,为变换后的串。
【样例输入】
5 3
10110
【样例输出】
11010
【样例说明】
初始时为 10110,变换 1 次后变为 11101,变换 2 次后变为 10011,变换 3
次后变为 11010。
【评测用例规模与约定】
对于 40% 的评测用例,1 ≤ _n _≤ 100, 1 ≤ _t _≤ 1000。
对于 80% 的评测用例,1 ≤ _n _≤ 1000, 1 ≤ _t _≤ 109。
对于所有评测用例,1 ≤ _n _≤ 10000, 1 ≤ _t _≤ 1018。
【解题思路:set去重找规律 40%+】
map记录第几个是那种样子
set标记,第二次遇上就退出循环
//变异
#include<bits/stdc++.h>
#include<iostream>
#define ll long long
using namespace std;
map<ll,string> m;
set<string> s;
int main(){
ll len,cnt,cntTemp;
cin >> len >> cnt;
cntTemp = cnt;
string str;
cin >> str;
ll flag = 0;
while(cnt--){
string temp = str;
for(int i = 1; i < len; i++){
temp[i] = (str[i-1]-'0')^(str[i]-'0') + '0';
}
//cout << temp << endl;
if(s.count(temp) == 1){
//cout<< "提前循环";
break;
}
s.insert(temp);
flag += 1;
m[flag] = temp;
str = temp;
}
if(cnt < 0){
cout << str;
}else{
ll index = cntTemp % flag;
if(index == 0) index = flag;
cout<< m[index];
}
return 0;
}
试题 H: 二进制问题[20 分]
时间限制: 1.0s 内存限制: 256.0MB
【问题描述】
小蓝最近在学习二进制。他想知道 1 到 _N _中有多少个数满足其二进制表示中恰好有 _K _个 1。你能帮助他吗?
【输入格式】
输入一行包含两个整数 _N _和 K。
【输出格式】
输出一个整数表示答案。
【样例输入】
7 2
【样例输出】
3
【评测用例规模与约定】
对于 30% 的评测用例,1 ≤ _N _≤ 106, 1 ≤ _K _≤ 10。
对于 60% 的评测用例,1 ≤ _N _≤ 2 × 109, 1 ≤ _K _≤ 30。对于所有评测用例,1 ≤ _N _≤ 1018, 1 ≤ _K _≤ 50。
【解题思路:dfs混 <60%的分】
暴力循环遍历可以拿稳30%的
dfs找可以混60%的分
但考试最后没空写完另一种情况了,估计30%左右
#include<bits/stdc++.h>
#include<iostream>
#define ll long long
using namespace std;
// 60位数可以表示1e18
// dp / 搜索
int temp[60] = {0},maxNum[60] = {0};
int pos[60] = {0}; //第几个i在哪个位置
string str;
ll N,K;
ll ans = 1;
int haveOne = 0,Cnt;
//第n个1
int find(int n){
for(int i = 0; i < Cnt; i++){
}
}
void dfs(int n){
if(n < 0) return;
int nowIndex = pos[n];
int tIndex = nowIndex + 1;
while(tIndex < Cnt && maxNum[tIndex] == 0){
swap(maxNum[tIndex],maxNum[nowIndex]);
ans++;
dfs(n-1);
swap(maxNum[tIndex],maxNum[nowIndex]);
tIndex++;
}
dfs(n-1);
}
int main(){
cin >> N >> K;
Cnt = 0;
while(N){
temp[Cnt++] = N % 2;
N/=2;
}
for(int i = 0; i < Cnt; i++){
maxNum[i] = temp[Cnt - 1 - i];
if(maxNum[i] == 1){
pos[haveOne++] = i;
}
//cout << maxNum[i] << endl;
}
//已有的大于需要的就舍去后面的1
if(haveOne > K){
int flag = haveOne - K;
while(flag--){
for(int i = Cnt - 1; i >= 0; i--){
if(maxNum[i] == 1){
maxNum[i] = 0;
break;
}
}
}
}
else if(haveOne < K){ //已有的小于需要的就从前面补
int flag = K - haveOne;
while(flag--){
for(int i = 0; i < Cnt; i++){
if(maxNum[i] == 0){
maxNum[i] = 1;
pos[haveOne++] = i;
break;
}
}
}
}
haveOne = K;
/\*for(int i = 0; i < haveOne; i++){
cout << pos[i];
}\*/
dfs(haveOne-1);
cout << ans;
return 0;
}
试题 I: 翻转括号序列[25 分]
时间限制: 2.0s 内存限制: 512.0MB
【问题描述】
给定一个长度为 _n _的括号序列,要求支持两种操作:
- 将 [Li, Ri] 区间内(序列中的第 Li 个字符到第 Ri 个字符)的括号全部翻转(左括号变成右括号,右括号变成左括号)。
- 求出以 _Li _为左端点时,最长的合法括号序列对应的 _Ri _(即找出最大的
Ri 使 [Li, Ri] 是一个合法括号序列)。
【输入格式】
输入的第一行包含两个整数 n, m,分别表示括号序列长度和操作次数。第二行包含给定的括号序列,括号序列中只包含左括号和右括号。
接下来 m 行,每行描述一个操作。如果该行为 “1 Li Ri”,表示第一种操作,区间为 [Li, Ri] ;如果该行为 “2 Li” 表示第二种操作,左端点为 Li。
【输出格式】
对于每个第二种操作,输出一行,表示对应的 Ri。如果不存在这样的 Ri,请输出 0。
【样例输入】
7 5
((())()
2 3
2 2
1 3 5
2 3
2 1
【样例输出】
4
7
0
0
【评测用例规模与约定】
对于 20% 的评测用例,n, _m _≤ 5000; 对于 40% 的评测用例,n, _m _≤ 30000;对于 60% 的评测用例,n, _m _≤ 100000;
对于所有评测用例,1 ≤ _n _≤ 106, 1 ≤ _m _≤ 2 × 105。
【解题思路: 压缩并查集+提前标记 <=100%】
因为每次查都会浪费时间,而且用例很大,所以每次进行调转操作后,就记录每个点对应的匹配点在哪
此时又有一种情况()()() 此时的1 3 5都是以6截至,本来应该是12 34 56匹配,但题目是最远匹配处,所以要延申后面的匹配
所以看成正常匹配时fa[1] = 2,fa[2] = 0,fa[3] = 4,fa[4] = 6;
而当fa[1]匹配时查看匹配点2右边的点是否还继续匹配,
fa[1] + 1表示第一个匹配的括号的右边第一个位
fa[ fa[1] + 1 ]表示看下右边还有没有继续匹配的
匹配的话就继续延申下去,找到头,然后返回路上都
fa[1] == 2 fa[2+1]!=0 继续
fa[3] == 4 fa[4+1]!=0 继续
fa[5] == 6 fa[6] = 0 回退 return 6
fa[3] = 6
fa[1] = 6
置换就直接换就行
#include<bits/stdc++.h>
#include<iostream>
#define ll long long
using namespace std;
map<int ,int > mp;
string str;
int num[1000005];
int n,m;
//初始化
void init(){
mp.clear();
stack<int> sta;
for(int i = 1; i <=n ; i++){
if(num[i] == 1){
sta.push(i);
}else if(num[i] == 0 && sta.size()){
int index = sta.top();
if(index != 0){
sta.pop();
mp[index] = i;
}
}else if(num[i] == 0 && !sta.size()){
sta.push(0);
}
}
}
//改造的压缩并查集
int findFa(int a,int deep){
if(mp[a] == 0 && deep == 0){
return 0;
}
if(mp[a] == 0){
return a - 1;
}
mp[a] =findFa(mp[a]+1,deep+1);
return mp[a];
}
int main(){
cin >> n >> m;
cin >> str;
//处理输入变成01串,方便倒置 !str[i]就行
for(int i = 0;i < n; i++){
if(str[i] == '('){
num[i+1] = 1;
}else{
num[i+1] = 0;
}
}
init();
for(int i = 0; i < m; i++){
int flag;
cin >> flag;
//倒置
if(flag == 1){
int a,b;
cin >> a >> b;
for(int j = a; j <= b; j++){
num[j] = !num[j];
}
init();
}else{
//直接查询
int a;
cin >> a;
cout << findFa(a,0) << endl;
}
}
return 0;
}
试题 J: 异或三角[25 分]
时间限制: 1.0s 内存限制: 256.0MB
【问题描述】
给定 T 个数 n1, n2, · · · , nT ,对每个 ni 请求出有多少组 a, b, c 满足:
- 1 ≤ a, b, c ≤ ni;
a ⊕ b ⊕ c = 0,其中 ⊕ 表示二进制按位异或;
长度为 a, b, c 的三条边能组成一个三角形。
【输入格式】
输入的第一行包含一个整数 T 。
接下来 T 行每行一个整数,分别表示 n1, n2, · · · , nT 。
【输出格式】
输出 T 行,每行包含一个整数,表示对应的答案。
【样例输入】
2
6
114514
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
【问题描述】
给定 T 个数 n1, n2, · · · , nT ,对每个 ni 请求出有多少组 a, b, c 满足:
- 1 ≤ a, b, c ≤ ni;
a ⊕ b ⊕ c = 0,其中 ⊕ 表示二进制按位异或;
长度为 a, b, c 的三条边能组成一个三角形。
【输入格式】
输入的第一行包含一个整数 T 。
接下来 T 行每行一个整数,分别表示 n1, n2, · · · , nT 。
【输出格式】
输出 T 行,每行包含一个整数,表示对应的答案。
【样例输入】
2
6
114514
[外链图片转存中…(img-wshbPK33-1715500143274)]
[外链图片转存中…(img-eL2hBY4y-1715500143274)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!