【周赛】第一周周赛——欢迎16级的新同学题解(题目出自codeforces 318A,546A,431C,665E,HDU 4104)
A题:
A题题目链接
A题题目描述:
Home W的数学
我们都知道,Home W的数学最厉害了。有一天,他又开始开动脑筋了,他想:“为什么数字总是要从1排列到n呢?”于是,Home W开始研究自己排列数字的方法。首先,他写下了1-n中所有的奇数(按照升序排列),然后他又写下了1-n中所有的偶数(按照升序排列),那么问题来了,在这样的排列方式下第k个数是什么呢?
输入只有一行,包括n和k(1 ≤ k ≤ n ≤ 1012).
注意:长整型声明形式为: long long a;
在这里的输入形式为:scanf("%I64d",&a); (或者使用cin也可)
输出形式为:printf("%I64d\n",a); (或者使用cout也可)
输出只有一行,输出第k个数即可。
10 3
5
样例说明(非输出部分): 按照Home W的排列方式即为{1,3,5,7,9,2,4,6,8,10},那么显然第三个数是5
7 7
解析:
题意很明显,我们可以分奇偶的情况进行考虑:
当n为偶数的时候,显然前n/2个数为奇数,后n/2个数为偶数,假设第1-n个数的下标分别是1~n,则前n/2个数分别为2*k-1,而后n/2
个数2*(k - n/2)。
当n为奇数的时候,则前(n+1)/2个数为奇数,后(n+1)/2-1个数为偶数(相当于在最后补上缺少的那个偶数,实际上不存在),同样的前
(n+1)/2个数分别为2*k-1,后(n+1)/2-1个数为2*(k-n/2)。
完整代码实现:
#include <cstdio>
typedef long long LL;
void solve(LL sum, LL index){LL mid = (sum + 1) / 2;if(index <= mid){printf("%I64d\n",index*2-1);}else{printf("%I64d\n",(index - mid)*2);}
}int main(){LL n,k;while(scanf("%I64d %I64d",&n,&k)!=EOF){solve(n,k);}return 0;
}
B题:
B题题目链接
题目描述:
QAQ和香蕉
QAQ是个吃货,这一次他来到了一个商店,想买w根香蕉,但是这个商店实在是黑,他需要支付k元买第一根香蕉,2k元买第二根香蕉....(也就是说,当他买第k根香蕉时,他需要支付i*k元)。
可是QAQ钱包里只有n元,你能帮助他计算一下,他要借多少钱才能买下w根香蕉吗?
第一行包括三个整数 k, n, w (1 ≤ k, w ≤ 1000, 0 ≤ n ≤ 109), 分别是第一根香蕉的单价,QAQ钱包里的钱总数,以及他想要买的香蕉总数。
输出只有一行,包含一个整数——QAQ需要借多少钱,如果他不需要借钱,输出0。
3 17 4
13
解析:
我们可以先计算w根香蕉的总价格为:k+2*k+...+w*k = (1+w) * w / 2 * k,因此如果QAQ钱包里的钱大于总价格,显然输出0,否
则的话则输出其差值。
完整代码实现:
#include <cstdio>void solve(int unitPrice,int allMoney,int bananaAmount){int borrowMoney = bananaAmount * (bananaAmount + 1) / 2 * unitPrice - allMoney;if(borrowMoney <= 0){printf("0\n");}else{printf("%d\n",borrowMoney);}
}int main(){int k,n,w;while(scanf("%d %d %d",&k,&n,&w)!=EOF){solve(k,n,w);}return 0;
}
C题
C题题目链接
题目描述:
QAQ的数学题
Home W说他数学很好,QAQ表示不服气,于是QAQ出了一道数学题给Home W做。题目很简短:给定n个数字,每个数字最多选择一次(也可以不选),问这n个数字不能组合得到的最小的值,并输出。
输入到文件结束( 即输入格式为 while(scanf(...)!=EOF)){ ... } )
第一行包含一个整数N(1 <= N <= 1000),第二行为N个整数Pi(0 <= Pi <= 10000).
输出只有一个整数,表示这n个数字不能组合得到的最小的值。
4 1 2 3 4
11
解析:
这道题的思路比较巧妙,顺着题目的角度去思考很难入手,因为这样的话要一个个的遍历过去,然后再计算已经遍历过的序列
能够表示哪些数,而且不好判断,因为可以从数组中任意选择数字,顺着题目的角度很难入手,那为什么我们不考虑相反的方向呢?
考虑数组a不能表示哪些数,那我们假如现在遍历到a[i]元素时,此时数组能组合成1~sum中的任意数字,那么此时考虑a[i+1],
那怎么才能知道数组a不能表示哪些数呢?很显然,由于此时数组已经能组合成1~sum中的任意数字,那么当a[i+1] > sum + 1时,则
此时sum+1相当于被跳过了。显然sum+1是不能被数组元素组合成的。而当a[i+1] <= sum + 1时,显然此时可以组合成的数字的范
围扩大为1 ~ sum + a[i+1]。所以抓住了sum+1这个临界条件,问题就解决了。
所以我们可以先排序,然后按照上述过程处理,排序的目的是为了找到最小的不能组合成的数字。
完整代码实现:
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX_SIZE = (int) 1e3 + 10;
int a[MAX_SIZE];void solve(int sum){for(int i = 0;i < sum;i++)scanf("%d",&a[i]);sort(a,a + sum);int ans = 0;for(int i = 0;i < sum;i++){if(a[i] > ans + 1){break;}else{ans += a[i];}}printf("%d\n",ans + 1);
}int main(){int n;while(scanf("%d",&n)!=EOF){solve(n);}return 0;
}
D题:
D题题目链接
题目描述:
running jump的树
我们都知道,running jump数据结构最厉害了,于是这一天,他又创造了一种新的数据结构,叫做k-树.那什么是k-树呢?首先,k-树时一个无限节点的树,意
思是说这棵树是可以不断往下延伸的,并且k-树有以下的性质:
(1).每个节点有k个子节点
(2).每条边都有一个权重,每条边的权重从左往右一次为1,2,3,...,k.
(感觉好神奇的样子
下图是3-树的一部分(因为节点是无限的,所以还可以往下无限延伸)
这时候,我们的running jump开始给我们出题了,他说:“从根节点开始,有多少条路径的权值之和为n呢?”然后他又想了想,感觉题目太容易了,于是又
加了一个限制条件,路径中至少要有一条边的权重大于等于d。那么聪明的Acmer,你能解决running jump给我们留下的这个问题吗?
由于结果可能过大,因此将结果对1000000007(109 + 7)取余后输出.
输入只有一行,包括三个整数,n, k and d (1 ≤ n, k ≤ 100; 1 ≤ d ≤ k).
输出只有一行,包含对1000000007 (109 + 7)取余后的结果.
3 3 2
3
3 3 3
1
4 3 2
6
4 5 2
7
解析:
由于题目给了限制条件,所以给我们的思考带来了一定的障碍,那么我们可以先将问题化简,如果没有至少含有一条权重大于
等于d的路径这个条件的话,该如何考虑呢?
如果没有了这个限制条件,那么就相当于从 1 ~ k 中选择一些数字,使得他们的和为n,每个数字可以选择多次,很直接的思路就是搜索,但是
搜索的时间复杂度是指数级的,而n,k范围比较大(n,k <= 100),因此搜索是不可取的。那考虑权值之和为j时,剩下需要解决的则是权值之和为n-j
的子问题。而n-j的子问题依旧可以按照以上方式继续分解,很显然,利用这种方式考虑子问题推出更大问题的解时,原问题不需要考虑子问题是如何
达到当前状态的。因此符合无后效性,并且原问题下的子问题的解也是当前找到的最优解(即统计好了所有路径和为j的路径条数),因此该问题符合最优
子结构性质。可用动态规划求解。
既然可用动态规划求解,我们利用刚刚找到的无后效性的状态,原问题拆分为j和n-j规模的子问题,而k-树共有k个分支。很显然我们可以推出状态
转移方程即是:
dp[n] = dp[n-1] + dp[n-2] + ... + dp[n-k];
再考虑限制条件:至少含有一条权值大于等于d的边,很显然,对于这种至少含有一个,一种,一条的一类的问题,正难则反,我们则考虑所有
的边权值都小于d,因此我们只要将权值和为n,分支数为k的总的路径数减去权值和为n,分支数为b-1(这样的话最大权值的边也就是b-1)的路径数
即得我们所求的答案。因此只要将状态转移方程增加一维表示分支数不同的状态值求解即可。
在确定状态转移方程递推原问题的过程中,确定一些状态的初始值,很显然,dp[0]即为根节点处,权值为0的只有在这一点,因此两种分支数的
情况下dp[0] 均等于1,最后,小心取模即可。
完整代码实现:
#include <cstdio>
#include <cstring>
const int MAX_SIZE = 105;
const int MOD = 1e9+7;
int dp[2][MAX_SIZE];void solve(int n,int k,int d){memset(dp,0,sizeof(dp));dp[0][0] = dp[1][0] = 1;for(int i = 1;i <= n;i++){for(int j = 1;j <= k;j++){if(i - j < 0){break;}else{dp[0][i] += dp[0][i-j];if(dp[0][i] >= MOD){dp[0][i] -= MOD;}}}for(int j = 1;j < d;j++){if(i - j < 0){break;}else{dp[1][i] += dp[1][i-j];if(dp[1][i] >= MOD){dp[1][i] -= MOD;}}}}printf("%d\n",((dp[0][n] - dp[1][n]) + MOD) % MOD);
}int main(){int n,k,d;// freopen("cf 431C.txt","r",stdin);while(scanf("%d %d %d",&n,&k,&d)!=EOF){solve(n,k,d);}}
E题:
E题题目链接
Value Dragon出难题了
有一天,Value Dragon觉得好无聊啊,所以决定出一道题目给自己做,于是他写下了一个含有n个元素的整型数组a,这n个元素分别是a1,a2,...,an。
然后呢,他就想啊,如果能找到一个连续的区间[l,r](1 ≤ l ≤ r ≤ n),使得该区间中所有的数的异或值大于等于k,那他就觉得这段区间是一个完美的区间。
那么问题来了,这样的区间总共有多少个呢?于是Value Dragon陷入了无尽的思考中......
第一行输入为两个数,分别是n和k (1 ≤ n ≤ 106, 1 ≤ k ≤ 109) — 分别表示整型数组元素的个数以及参数k的值
第二行输入为n个整数 ai (0 ≤ ai ≤ 109) — 数组a的n个元素
输出只有一行,表示在数组a中,这样的完美区间有多少个。
3 11 2 3
5
3 21 2 3
3
3 31 2 3
2
解析:
这道题考察的是异或,那么对于异或,有一些重要的性质:
1.0 ^ 1 = 1,0 ^ 0 = 0,以上说明,0异或任何数的结果均为0.
2.n^n = 0,这说明任何数与其自身异或后的结果必然为0.
利用以上两个性质,显然我们可以将异或前缀和处理一下,这样的话任意两个前缀和进行异或后的结果以及其所有预处理好
的前缀和自身,就可以将所有连续区间遍历完毕。这样直接处理的话时间复杂度是O(n^2),而n <= 10 ^ 6,这样做显然是超时
的。那么怎么样降低时间复杂度呢?
试想,对于任意一个前缀和区间,我们要做的是,找到其他满足条件的前缀和区间或者0,使得两者异或后的结果大于等于k,
即对于任意的前缀异或和sum,我们要做的是找到 sum ^ [] >= k,因此这是一个匹配的过程,而对于两个数字的比较,除了直接判
断之外,我们还可以通过比较其二进制的形式来比较其大小。比如说:数字7和数字6,四位二进制形式分别为(0111) 和 (0110),用
这样的方式我们当比较到两个数的最后一位时,才能确定 0111 > 0110,而对于其二进制的存储及比较,我们可以看成对01的存储,
然后再匹配。
那么这个过程我们就可以用字典树实现,初始时字典树只含有根节点,然后利用性质1,插入数据0。而后再一位一位的匹配比
较即可。注意k值的范围,从而确定树的深度。具体详细解释可看代码注释:
#include <cstdio>
const int maxMoveStep = 29; //由于kmax = 1e9,而2^30=1073741824,因此最多移位次数为29
typedef long long ll;
struct node{ll weight; //表示节点权值,也就是说该节点下有多少个满足条件的数字node *next[2]; //分别表示01字典树的两个数位,0 1node(){weight = 0;next[0] = next[1] = NULL;}
};void trieInsert(node *root,int value){node *p = root;for(int i = maxMoveStep;i >= 0;i--){int bit = value >> i & 1; //从最高位存至最低位if(!p -> next[bit]){ //如果对应数位的节点不存在,则新建该节点p -> next[bit] = new node();}p -> weight++;p = p -> next[bit]; //存在则直接往下继续遍历即可}p -> weight++;
}int trieQuery(node *root,int prefixSum,int k){node *p = root;ll ans = 0;for(int i = maxMoveStep;i >= 0;i--){int pBit = prefixSum >> i & 1;int kBit = k >> i & 1;if(kBit){ //如果k的该位为1,要使最后结果大于等于k,那么p指针就只能往pBit^1的方向移动pBit ^= 1;}else{ //如果k的该位为0,显然pBit^1方向的子树均满足条件,则可以将其结果计算进来//然后p指针再往pBit方向遍历,考虑剩下半边子树满足条件的部分if(p -> next[pBit^1]){ans += p -> next[pBit^1] -> weight;}}p = p -> next[pBit];if(!p){return ans;}}return ans + p -> weight; //考虑到了根节点,因此还要加上异或后结果等于k的部分
}void solve(int n,int k){int number,sum = 0;ll cnt = 0;node *root = new node(); //01字典树的根节点trieInsert(root,0); //插入初始值,以方便考虑到所有的情况,包括前缀异或和其本身for(int i = 0;i < n;i++){scanf("%d",&number);sum ^= number;cnt += trieQuery(root,sum,k);trieInsert(root,sum);}printf("%I64d\n",cnt);delete root;
}int main(){int n,k;// freopen("cf 665E.txt","r",stdin);while(scanf("%d %d",&n,&k)!=EOF){solve(n,k);}return 0;
}
总结:这次题目的总体难度不大,考察的知识点不多,但是01字典树还是很常用的,很多异或的问题都是用01字典树解决,第四
题的dp题目属于比较简单的dp题,需要找相似的子问题,然后状态转移方程也不难推导出来。
【周赛】第一周周赛——欢迎16级的新同学题解(题目出自codeforces 318A,546A,431C,665E,HDU 4104)相关推荐
- 第一周周赛D - Problem D HDU - 2039
D - Problem D HDU - 2039 给定三条边,请你判断一下能不能组成一个三角形. Input 输入数据第一行包含一个数M,接下有M行,每行一个实例,包含三个正数A,B,C.其中A,B, ...
- 【MATLAB】P图神器,初露锋芒:第一周作业
做完第一周Matlab作业,深感MatLab之强大.(都第几周了,才做第一周作业-) 不在上图像处理这门课的同学,也可以试试在Matlab敲这些代码哦~ 用Matlab P图可有意思呢~ 第一周是粗略 ...
- 20210627:力扣第247周周赛(上)
力扣第247周周赛(上) 题目 思路与算法 代码实现 写在最后 题目 两个数对之间的最大乘积差 循环轮转矩阵 思路与算法 两个数对之间的最大乘积差:排序相减即可 循环轮转矩阵:模拟即可,注意逆时针和顺 ...
- 20210426:力扣第238周周赛(上)
力扣第238周周赛(上) 题目 思路与算法 代码实现 写在最后 题目 K 进制表示下的各位数字总和 最高频元素的频数 思路与算法 第一题是数学题,考察各进制与十进制数的相互转换,不多赘述,连续取余求和 ...
- 20210424:力扣第237周周赛(下)
力扣第237周周赛(下) 题目 思路与算法 代码实现 写在最后 题目 单线程 CPU 所有数对按位与结果的异或和 思路与算法 第一题属于一类cpu流水线题目,实现常规方法堆加排序即可. 第二题属于数学 ...
- 20210217:力扣第228周周赛(上)
力扣第228周周赛(上) 题目 思路与算法 代码实现 写在最后 题目 生成交替二进制字符串的最少操作数 统计同构子字符串的数目 思路与算法 第一题需要注意不能直接翻译题意,直接翻译的代码存在先入为主定 ...
- 20210201:力扣第226周周赛(上)
力扣第226周周赛(上) 题目 思路与算法 代码实现 写在最后 题目 盒子中小球的最大数量 2. 1743. 从相邻元素对还原数组 思路与算法 第一题简单的数数问题,python比较方便,使用str函 ...
- 20201008:力扣209周周赛题解(下)
力扣209周周赛题解(下) 题目 思路与算法 代码实现 写在最后 题目 可见点的最大数目 使整数变为 0 的最少操作次数 思路与算法 第一题注意添加角度时atan2的范围是正负π,因此注意差值可能超出 ...
- 20201007:力扣209周周赛题解记录(上)
力扣209周周赛题解记录(上) 题目 思路与算法 代码实现 复杂度分析 题目 特殊数组的特征值 奇偶树 思路与算法 第一题单纯的暴力就行,应该是可以用二分之类的改善复杂度,没必要赘述,直接上暴力遍历, ...
最新文章
- docker命令行解析以及如何向服务器端发送请求(docker源码学习一)
- 砝码问题之一(回头发现貌似多重背包)
- boost::math::fisher_f用法的测试程序
- 一款基于jquery带百分比的响应式进度加载条
- C++学习之路: 线程封装(基于对象编程)
- java实现同步的两种方式
- anaconda必须安装在c盘吗_Anaconda及tensorflow、pytorch安装记
- Lenovo DS存储Linux下ISCSI 多路径映射配置
- win7用html做桌面,win7系统怎么制作主题桌面 win7系统制作主题桌面方法
- Mindspore | lenet 模型代码
- Tensorflow实现网络---Resnet
- 小程序自定义底部菜单栏
- 利用java计算长方形的面积
- CC00028.CloudKubernetes——|KuberNetes二进制部署.V06|5台Server|——|etcd配置|
- Git用户手册--GitHub
- linux如何合并文件
- 弘辽科技:拼多多店铺可以改名字吗?店铺名字怎么取?
- 手动安装Eclipse插件
- python画图设置字体_【转】matplotlib画图时的中文设置
- (附源码)计算机毕业设计SSM基于web的图书借阅管理系统
热门文章
- php 使用rand函数产生一个随机数
- 国外创意名片设计欣赏的200佳网站推荐(系列十三)
- 台式计算机机箱的作用,如何选购台式电脑机箱?小白装机选购电脑机箱知识指南...
- 前向最大匹配分词FMM
- 智能手机的发展演变,智能手机对人类带来的影响
- js 前端 时间戳转字符串 2019-08-18T16:00:00.000Z 转换 2019-08-19 00:00:00
- 奇异量子物理系统:它既在“绝对零度”之下,又在“绝对高温”之上
- cesium实现动态扩散墙效果(基于turf)
- sap 获取计划订单bapi_【原创】2011.09.18 SAP 订单中修改订单净价
- 计算机组成原理算术逻辑实验,《计算机组成原理》算术逻辑运算实验报告.doc...