10.12至10.14考试题
10.12
NOIp信心赛(QBXT Day2)
NOIp信心赛
为什么这个p是小写的?
异或
【问题描述】
给定序列A,计算\(\displaystyle\sum_{i=1}^n\sum_{j=1}^n{A_i\mathrm{and}A_{i+1}\mathrm{and}\dots\mathrm{and}A_j}\)。
【输入格式】
从文件 a.in 中读入数据。
第一行一个整数N。
第二行N个整数描述A。
【输出格式】
输出到文件a.out中。
一行输出答案。
【样例输入】
3
1 2 3
【样例输出】
8
【样例解释】
1+2+3+0[1~2]+2[2~3]+0[1~3]=8
【数据规模】
对于10%的数据,\(N\le100,A_i\le1\)。
对于20%的数据,\(N\le100\)。
对于30%的数据,\(N\le1000\)。
对于40%的数据,\(N\le100000\)。
以上的数据档互不相交。
对于所有的数据,满足\(1\le N\le10^5,0\le A_i\le2^{31}-1\)。
【题解】
二进制位是互不相干的,所以可以拆位处理。
一个区间有贡献,当且仅当区间内没有0。
所以可以处理出所有全为1的极长的区间,区间内贡献即为n(n+1)/2。注意每一位要乘以它的掩码。
细胞分裂
【问题描述】
你正在一个长度为N的环状台上进行细胞分裂的实验。
这个试验台一共有N个格子,每个格子被标号为0..N-1,其中1格子与i-1和i+1号格子相邻。值得注意的是,当i=0时,i−1视为N−1,同理i=N−1时,i+1视为0。一开始,在某些格子上可能会有1个细胞,也可能没有。
在每一秒钟的末尾,每一个细胞会分裂成2个细胞,并且一个到达i-1位置,一个到达i+1位置。
现在已知最开始的细胞存在情况,假设第一个时刻末开始第一次分裂,求第T个时刻末分裂结束后每个格子的细胞个数。
为了方便你只需要输出每个格子的细胞数量对P取模的值。
【输入格式】
从文件b.in中读入数据。
第一行三个整数N,T,P,分别表示环的长度,时刻和模数。
第二行一个01串s描述这个串。
【输出格式】
输出到文件b.out中。
N行,第i行输出编号为i-1的格子的答案。
【样例输入】
5 2 2
00100
【样例输出】
1
0
0
0
1
【样例解释】
一开始每个格子的个数为0 0 1 0 0
在第一个时刻变为0 1 0 1 0
在第二个时刻变为1 0 2 0 1
【数据规模】
对于30%的数据,N,T≤1000.
对于20%的数据,N≤18,P=2
对于10%的数据,P=2,T=402653184.
对于10%的数据,P=2
对于30%的数据,没有任何限制。
以上数据档互不相交
所有数据满足:1≤N≤100000, 0≤T≤10^9,2≤P≤ 10,且P一定是质数.
【题解】
本来以为是矩阵快速幂板子题,结果一看数据范围,连多项式取模都过不了。
反正是一个特别奇怪的规律
待更
递归
【问题描述】
ZYB是一个非常强大的ACM手,他与别人交流的方式也与众不同-利用数字串,并且他将对这个数字串进行加密处理。
这个加密处理的方式如下:
信息中会涉及到前K种大写字符以及所有的数字,并且对于每一个大写字母,有一个唯一的替换串。
例如A->BB,B->CC0,C->123都是合法的替换串,分别表示A=12312301231230,B=1231230,C=123.
现在ZYB告诉了你一个大写字母A。辣鸡出题人立刻就破解出了他的原文,但是辣鸡出题人还想知道,对于这个数字串,有多少非空的子串满足:
1.这个子串为0或者没有前导0.
2.把这个子串看作10进制数后模n为0.
你只需要输出合法的子串个数模r的值。
【输入格式】
从文件c.in中读取数据。
第一行,两个正整数n,r.
第二行,一个正整数k.
接下来k行,每行一个替换式\(S_i\)。保证按照A,B...的顺序给出。并且字符X的替换串中只会出现比X大的字符。
【输出格式】
输出到文件 c.out 中。
一行一个整数,输出答案。
【数据规模】
对于20%的数据,最终串的长度不超过150.
对于30%的数据,最终串的长度不超过100000.
对于20%的数据,没有0字符。
对于30%的数据,没有任何限制。
以上数据档互不包含。
对于100%的数据,\(2\le n\le 30,2\le r\le 10^9,1\le k\le 26,4\le |S_i|\le100\)。
【题解】
贼他妈复杂的一道题。
考虑维护一个区间的信息。
因为本题在组成一个替换的过程,可以视为合并若干个区间的过程,所以我们维护的信息要求能够方便合并。
对于维护下列信息:
contribution 区间内的串的贡献
len 10的[区间长度]次方对n取模
tot 区间代表数对n取模
head[i][j] 满足10的[长度]次方对n取模为i,代表数对n取模为j的前缀的个数
tail[i] 满足代表数对n取模为j的后缀的个数
对于合并区间,即给定两个区间a和b的信息,需要计算ans的信息的操作,我们:
对于len:ans.len=(a.len*b.len)%n,根据初中课本,同底数幂的乘法底数不变指数相加,反过来即可。
对于tot:ans.tot=(a.tot*b.len+b.tot)%n。把两个数合并就相当于前面的数左移后面数的位数,再相加(十进制下的左移就是乘以10)
对于contribution:根据cdq分治的思想,ans的贡献就是a的贡献加b的贡献加跨过a和b中间点区间的贡献。
枚举a的后缀a.tail[k]
和b的前缀b.head[i][j]
,如果这两部分接上之后模n为0,即(k*i+j)%n=0
,那么就对答案禅城a.tail[k]*b.head[i][j]
的贡献。
对于head
:显然ans的head是由两部分组成,第一部分是a的前缀,第二部分是由整个a区间和b的前缀组成的前缀。第一部分直接从a的head那里copy一下即可,第二部分就要枚举b的head,转移如下:ans.head[(i*a.len)%n][(a.tot*i+j)%n]+=b.head[i][j]
对于tail
:同上,ans的tail也是两部分组成,第一部分是b的后缀,第二部分是有整个b区间和a的后缀组成的后缀。第一部分同上直接copy,第二部分也推一下式子:ans.tail[(i*b.len+b.tot)%n]+=a.tail[i]
这样我们的区间合并就完成了。然后我们先处理出所有只包含一个数字的区间(注意0不能做一个区间的开头,其实是不能做一个后缀的开头,因为对答案的贡献形式只能是一个后缀和一个前缀)。我们把0的tail设为0,其它的设为1即可。head[10%n][num%n]
可以都设为1。注意如果num%n=0
那么这区间也有1的贡献。
然后从最大的字母开始计算这个字母缩代表的区间的信息,直接大力区间合并即可。复杂度期望O(sumS*n^3)
std的代码好像很简单的呢
10.13
zmj
canteen
背景
雷哥再也忍受不了吃不上饭的日子了,便自己靠武力智慧向主任申请开了一个自己的窗口,这样雷哥就不用去排队打饭,当然雷哥为了钱弘扬中华美德,也供其他的人来吃。因为饭菜好吃,来的人越来越多,排起了长队。当然,雷哥作为这个窗口的主人,一定要解决这个问题,他的解决方式很特殊,就是让妹子排在队伍的前面,这样更方便提高他在妹子面前的面子。即使这样,也有好多贪吃的男生来这里打饭并提出了意见,比如说wyl,他提的意见被雷哥全部否定,他一气之下说“别跟我扯”,又被雷哥治服了,从此男孩子只能乖乖的排在队伍后面。。。
题目描述
雷哥每次可以安排他的小弟去调换相邻两个人的位置,因为雷哥的小弟有无数个,所以每单位时间可以将任意相邻的两个人交换,妹子用'a'表示,汉子用'b'表示,问最短多少时间可以将所有的妹子都排在前面,(不懂的可以去看样例解释)。
输入
一行字符串(只包含'a','b')。
输出
一个整数t,表示最短时间。
输入样例#1
baba
输出样例#1
2
解释
baba--1-->abab--2-->aabb
输入样例2
baab
输出样例2
2
解释
baab--1-->abab--2-->aabb
数据点 | 字符串的长度 | 特殊性质 |
---|---|---|
1 | 10 | 无 |
2 | 100 | 无 |
3 | 1000 | 无 |
4 | 5000 | 无 |
5~6 | 50000 | 无 |
7~10 | 1000000 | 无 |
题解
神仙题,用各种xjb推法都能退出来正确的结论。
首先不难想到一个贪心包丽丽:贪心地交换前面的b和后面的a。
我的思路是:把所有的b当做空格,就相当于让a一个劲往前走。例如:
aaaa a .
a aaa a .
a a aaa .
a a a aa.
a a a a a
a a a a a
a a a a a
aa a a a
aaa a a
aaaa a
aaaaa
正常情况下是每个a每一秒向前走一格,但是最后一个a因为前面的a的限制,需要等待才可以走。一串稳定的a是这些a之间有一个空格。所以我们就可以让最后一个a一开始时候在.的位置,这样就保证他是匀速前进的。我们对于每一个a,按照每个a的间隔为1来计算最后一个a应当的的位置,并取max就行了。注意一开始就在原位的不用处理。
bobek
背景
终于到了假期,但是坐在沙发上看着电视,真是无聊。一会儿电话响了,是我的好朋友bobek的电话,“一起去看球吧”,“好啊,反正也是无聊”,“10分钟之内到XXX体育馆“,”没问题“。我便拉上睡懒觉的王小呆出发了。现场真是人山人海,队伍排了好长,谁知bobek有VIP,直接就可以买票,到了VIP售票口,面对种类繁多的门票,我们有犯了愁,不知道买哪些票。王小呆突然站出来,“让我来吧”,王小呆拿出随身携带的电脑,通过 找到了你(原来是请别人解决,真不要脸)。。。
题目描述
王小呆告诉你一共有N种票,并告诉你每种票的价钱\(w_i(w_i\le10^{16})\),和bobek所拥有的钱M。问你用M元钱,bobek有多少种购票方案(如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同,不买票也算一种方案)。
输入格式
第一行,两个正整数N和M(\(1\le N\le40,1\le M\le10^{18}\)) ,表示买票的种数和 bobek所拥有的钱。
第二行, 个以空格分隔的正整数,均不超过\(10^{16}\),代表每场比赛门票的价格。
输出格式
一个整数表示方案数。
输入样例
5 1000
100 1500 500 500 1000
输出样例
8
样例解释
扫兴,不看球了,回家上洛谷透彻吧
-
买一张100块的
-
买一张500块(第三种票)的
-
买一张500块(第四种票)的
-
买一张100块的和500块(第三种票)的
-
买一张100块的和500块(第四种票)的
-
买两张500块的
-
要的就是奢侈,买一张1000块的
一共8种
数据范围
数据分布 | n | m |
---|---|---|
4% | 20 | 10^6 |
14% | 20 | 10^18 |
26% | 40 | 10^6 |
100% | 40 | 10^18 |
注意:数据后来有更改,现在的百分比是人工数出来的,可能有误,但一定会有部分分(已测试过)。
题解
可以背包转移
正解是meet in the middle思想,我们把n个数分两半搜,把最后答案存在两个数组里,每次搜都是1048576不会超时。把第二个数组sort一下,每次对应第一个数组,在第二个数组里二分upper_bound即可。
这里rqj提供了一种更快的方法,我们维护两个队列光搜,并保证这两个队列是单调的。每次搜的时候,从两个队列取最小值,并把扩展出的不选放到一个队列里,选放到另一个队列里,这样保证队列是单调的,就能保证最后处理出的两个数组都是排好序的
既然两个数组都排好序了,那么我们就可以维护两个指针xjb乱指,就能线性求出结果了。
GXZlegend的博客里好像只有第一种解法诶
stone
背景
王小呆被自己的梦迷失了心智,走火入魔,小zager不能置之不管,便踏上了寻找王小呆的旅程,谁知王小呆为了不让任何人进入自己的梦中,设置了一个迷宫,而且他可以操纵这个迷宫,让来到这里的人民都迷失在迷宫了,成为王小呆的奴隶。。。。
描述
王小呆设置的迷宫非常奇特,有N个点,N-1条边,他可以对迷宫中的每个点发起M次操作,例如:让某个点出现一块“金刚碧·清醒石”( xjb起的名字),也可以让某个点的“金刚碧·清醒石”消失,还能发出“迷失之雾”。但小zager要想不迷失在王小呆的梦了,必须在王小呆发出“迷失之雾”时快速去收集所有的“金刚碧·清醒石”,但小zager不用自己去动手,因为他有一个小宠物enceladus,可以快速的收集“金刚碧·清醒石”,但必须保证你告诉enceladus所有“金刚碧·清醒石”所在的点连通所需的边集的总长度最小是多少,因为enceladus是不会走冤枉路的。
输入格式
第一行 一个数N ;接下来N-1行,每行3个整数u,v,w表示u和v之间有一条长度为w的双向边;
接下来 一个数 M;
接下来M 行(三种格式之一):
insert x
:表示点x上出现了“金刚碧·清醒石”;
destroy x
:表示点x上的“金刚碧·清醒石”被摧毁;
enceladus
:你要告诉 当前所有“金刚碧·清醒石”所在的点连通所需的边集的总长度最小是多少
输出格式
对于每一个 enceladus
操作都输出一个整数表示答案。
输入样例
6
1 2 3
2 3 4
4 5 2
1 4 6
4 6 7
9
insert 2
insert 4
enceladus
insert 5
enceladus
destroy 4
enceladus
insert 6
enceladus
输出样例
9
11
11
18
提示
第一次询问时(是一张图,懒得摆了)
数据范围
n | m | 特殊性质 |
---|---|---|
10 | 10 | 无 |
100 | 100 | 无 |
1000 | 1000 | 无 |
10000 | 10000 | 一条链 |
20000 | 20000 | 菊花图 |
50000 | 50000 | 无 |
100000 | 100000 | 无 |
100000 | 100000 | 无 |
50000 | 50000 | 无 |
100000 | 100000 | 无 |
题解
这是一道结论题。我们求出每个点的dfn来。对于插入和删除操作,把这个点的dfn加到一个红黑树里,答案是中序遍历相邻的节点之间的路径长度和/2(包括首末节点)。注意要动态维护答案。
好吧好理解但是不太会证明啊。这是比较烦的一件事。
也不太难证明吧,这里试一下。就是考虑我们dfs的过程,dfs出来了一个dfn对不对,就是每个点访问的先后次序。如果我们按照set里面dfn的次序依次访问节点,路径就是dfs路径的一条子路径?不能叫子路径,大概就是一段段区间的和。这能保证不走多余的路。这样我们走出来了一条环,环/2就是树的边长度和。
10.14
qbxt-dll
sword
【题目描述】
小林和亮亮各有一把光剑, 长度分别为 a 和 b, 他们拿光剑进行比试。 每一回合, 长光剑会砍向短光剑, 砍完后, 短光剑完好无损, 而长光剑则被截成两段,被截去的长度恰好等于短光剑的长度。 若两把光剑长度相等, 则比试结束。 请问小林和亮亮将比试多少回合?
【输入格式】
第一行一个整数 T, 表示数据组数。
接下来 T 行每行两个正整数 a, b, 表示初始状态光剑的长度。
【输出格式】
每组数据输出一个整数, 表示能进行几个回合的比试。
【样例输入】
3
18
3 7
6 6
【样例输出】
7
4
0
【数据规模】
对于 40%的数据, 0 < a, b <= 1000, 1 <= T <= 20;
对于 100%的数据, 0 < a, b <= 10^18, 1 <= T <= 1000。
【题解】
不难写出一个递归暴力:
int fuck(int x, int y)
{
if (x == y)
return 0;
if (x > y)
return fuck(x - y, y);
if (x < y)
return fuck(x, y - x);
}
我们发现,对于x-y会一直减下去,直到减到(x-1)%y+1,不能减为止,而换为y减去剩下的那个数字。减法的次数为(x-1)/y。这有点像我们的欧几里得算法求gcd。所以写一个类似于欧几里得算法的东西:
int fuck(int x, int y)
{
if (x == y)
return 0;
if (x > y)
return fuck((x - 1) % y + 1, y) + (x - 1) / y;
if (x < y)
return fuck(y, (y - 1) % x + 1) + (y - 1) / x;
}
当然也可以写成辗转相除的性质。复杂度和欧几里得算法等同,是log的。注意开long long。
zero
【题目描述】
小林拥有 2 个集合, 亮亮拥有 3 个集合, 这五个集合大小相等, 且集合中包含的都是整数。 现在他们两个要进行心算比赛。 比赛的规则是, 将这五个集合放在一起, 谁能先从每个集合中各选一个数, 使得选出的五个数之和为 0, 谁就获得胜利。 由于这五个集合都不小, 而小林和亮亮事先并不知道是否能存在这样的五个数, 因此他们决定先把五个集合都交给你, 由你来编程判断是否存在符合条件的五个数。
【输入格式】
第一行一个整数 N, 表示集合的大小。
接下来五行每行 N 个整数, 表示这五个集合内的元素。
【输出格式】
如果能找到符合条件的五个数, 则输出“YES” , 否则输出“NO” 。
【样例输入】
31
-2 9
-1 2 1
-3 5 1
-1 7 6
-4 -1 -7
【样例输出】
YES
【数据规模】
对于 30%的数据, 1 <= N <= 20;
对于 50%的数据, 1 <= N <= 100;
对于 100%的数据, 1 <= N <= 200, -10^8 <= a[i] <= 10^8, a[i]为集合中元素。
【题解】
由于直接搜索肯定会TLE,所以我们考虑双向搜索(其实直接for循环就行了),把前两组集合捆成一坨,后三组捆成一坨,前两组搜完了把所有结果存到一个哈希表中,搜后三组直接查表就行了。当然可以存到数组中排序,搜后三组时候二分,但是有可能会T(常数大时)。
2048
【题目描述】
小林和亮亮最近正在重温 2048 这款游戏。 由于他们的游戏水平高超, 觉得没有什么挑战性, 于是决定自己设计一款类似的游戏。
他们设计的游戏中, 数字排成了一个线性的序列, 每次玩家可以将序列中任意两个相同的数 a 合并, 成为一个新的数 2*a, 当合并出 2048 时游戏即获得胜利。 设计完后, 他们发现这个游戏比原来的版本更加简单了, 于是他们就开始计算, 对于一个给定的长度为 n 的序列, 它共有多少子序列可以合并出 2048。 请给出这个数模 998244353 后的值。
【输入格式】
第一行有一个整数 n。
第二行 n 个整数 Ai, 表示数列中的数。
【输出格式】
一个整数, 即为所求的答案。
【样例输入】
2
2048 2048
【样例输出】
3
【数据规模】
对于 40%的数据, n <= 20;
对于 70%的数据, n <= 500;
对于 100%的数据, 1 <= n <= 100000, 1 <= Ai <= 2048。
时间限制3秒
【题解】
玩过2048的都知道,要想合成2048,必须用2的幂次合成,并且所有合成的原料的和就是2048。所以我们可以dp,设f[i][j]
代表选前i个数,组成j能有多少种方案。方程不难写,f[i][j]=f[i-1][j]+f[i-1][j-a[i]]
,但是这里建议刷表(推表)。注意到所有数的和可能很大,当数的和大于等于2048时,后边不管怎么加数都没用了,所以可以直接统计到答案里,注意乘上后面数的个数。由于本题时限3秒,这么能过。注意开滚动数组。
还有一种做法就是组合数,由于序列是无序的,我们可以统计每一种2的幂的数量,设f[i][j]
为前i种2的幂所有的数组成j的方案,这个的转移就是一个组合数了,并且刷表(推表)要比较好转移。但是由于有组合数,还要打阶乘逆元快速幂。。。
注意到有的数不是2的次幂,他选不选不影响答案,所以答案乘以2的这种数字个数次幂。