杂题

我这个杂题可能是真的有点杂。但愿不要成为质量最低的一次。

最开始找到了几道很难的毒瘤题把自己恶心到了,所以准备的都不是特别难。(可能想不到,但应该听得懂)

因为是从$CF$上找的题,所以没有部分分想着就很不爽,所以我自己瞎加矮啦一点方便思考。如果产生了误导不要怪我(逃

并没有设置签到分,所有子任务都和正解相关且大致是按照相关程度小到大排序的,可以尝试一档一档的想想,如果提问的话想到哪一档说哪一档就成)

讲题的话还是听懂的$1$(跪求五星好评的感觉)没听懂的扣$0$原题一眼杀的扣$3$然后需要复读的就丢个$2$上来吧(我觉得这个选项最人性化,至少对我是的)

如果需要的话那最好指明自己听懂到哪。

有些担心平时自己问题最多现在我自己来讲题了效果会不会很差。$RNB$做好心理准备。

 

$1110H$

大意:给出$l,r,n$。要求你构造出一个长度为$n$的数串,最大化这个数串的【不含前导$0$且数值介于$[l,r]$的子串】的数量。多解输出字典序最小的。

只输出满足条件子串最大个数不需要构造字符串可以拿到部分分数。

$subtask1:l=r$。且只需要输出子串数。

$subtask2:10 \le |ll,|r|\le 99$

$subtask3:l=0$

$subtask4:|l|=|r|$(长度,不是绝对值。$l,r$均不含前导$0$)

$subtask5:l ,r \le 10^{600}, n \le 1000$。无特殊限制。

(怕你们说我卡常所以其实数据范围有一小点削弱,原范围$l,r,\le 10^{800},n \le 2000$。就当评测机带劲吧)

 

可能有点清新的数据结构。

首先看第一个子任务,也就是好的子串只有一种,那么你就会尽可能的在当前字符串末尾添加这个数串。可能有重叠部分。

其实就是$KMP$暴力求个最大$border$就可以了。每次就把原串接在自己之后并尽量节约长度出现次数就是最多的。

为什么只需要输出个数不需要字符串?因为可能存在多重$border$你不用最长的那一个可以使字典序最小。

应该是可以做但是和正解关系不大,没必要想下去。这个子任务只是把你拉到 模式串匹配用的字符串数据结构/算法上。

第二个子任务要求的串只有两位,直接记录末位然后$dp$就可以了。构造方案也需要按照$dp$数组倒推回去。这个子任务用于启发想到$dp$。

其实差不多了。能套上$dp$的模式串匹配用的字符串数据结构也就那么几种,比较适用于这道题的就是$AC$自动机了。。。

注意这道题的数据范围不大,可以考虑$n^2$级别的做法。设$dp[i][j]$表示目前已经用了字符串的$i$位当前在自动机的$j$节点此时的最大答案。

然而先别着急$AC$自动机还没建出来呢,总不能把$r-l$个字符串全丢进去。继续往后想。

子任务$3$是不考虑下界。举个例子,如果$r=2333$考虑哪些子串是合法的。(设$x$表示一个[0,9]的数字,$y$表示$[1,9]$的数字)

$x,yx,yxx,1xxx,20xx,21xx,22xx,230x,231x,232x,2330,2331,2332,2333$

就是说,要么原串更短那么就随便构成子串,否则一定都是前面几位和$r$一模一样,从某一位开始岔开且比$r$的那一位小,后面的位随便填。

发现用这种方式去表示来表示所有可能的串的话一定只有$10|r|$个,对于每个岔开的位最多$10$个。这些东西从大小上来讲是可以丢进$AC$自动机的。

问题在于通配符怎么丢?不用丢,你不在意这个串是在哪个位置结束的,你只在意它出现了多少次。所以我们只关心通配符串的长度。

直接开个$vector$之类的丢在$AC$自动机上就好了。若当前已匹配长度为$i$通配符长度为$j$,若$i+j \le n$就可以贡献$1$个串。

每一轮遍历整个$AC$自动机一次,节点个数$10|r|$,转移数$100|r|$。总复杂度$100n|r|$。(通配符一共只有$10|r|$个所以每一轮都遍历一下复杂度是对的)

考虑上$l$的话其实变化不大,如果长度相等会好做一些只用考虑某一种特定长度的串。往$AC$自动机里插入的时候上下界都卡一卡就好了。

如果长度不等的也没关系,把限制拆成$[l,10^{l+1} ) ,[10^{|l|+1},10^{|r|}),[10^{|r|},r]$中间那个随便选两边的和$l=0$做法类似就可以了。

 

$1305G:$

大意:$n$个人,第$i$个人年龄是$a_i$(看数据范围单位可能是小时?)。然后有个邪教组织。每次可以选择一个人让他加入邪教组织,但不会增加邪教影响力。

然后也可以让已经在邪教组织里的人$x$传教,选择他的一个还没有加入邪教的朋友让他也加入邪教,并产生$a_x$的影响力。

由于不同年龄的人有代沟,所以$x,y$是朋友当且仅当$a_x \ and \ a_y = 0$(按位与,虽说看起来代沟会变大的样子)

最大化邪教总影响力。(题意可能有一定偏差但是不影响做题)

下面的某些部分分只是便于思考,可能由于数据范围的存在数据并没有办法造,不要在意细节

$subtask1:a_1=0$

$subtask2:$朋友关系是一棵树(不要通过数据特殊性猜测$n$很小来解决问题,假装$n$一直开满)

$subtask3:a_i < 2^{15}$

$subtask4:n,a_i < 2^{18}$

 

首先子任务$1$看起来莫名其妙的,这个人来干啥的?他能给所有人传教但是给别人别人传教也和没传一样,被传教一定会选择年龄最大的那一个。

所以其实把答案提出来把他删掉也没事。对,删掉也没事,所以加上也没事。所以我想表达的是在后面如果有需要,为了方便处理,可以把所有直接加入组织的人看作是被$0$这样的人传教的。

这样传教关系图就一定联通了,会更好处理。

不只是联通,看$subtask2$,稍微想想就知道,传教关系其实就是树。然后这样产生的总权值就是父亲的权值。那么总答案就是每个点的$a$乘上度数$-1$(去掉父亲边)

$\sum\limits_{i=1}^{n} a_i (deg_i-1) = \sum\limits_{i=1}^{n} a_i deg_i - \sum\limits_{i=1}^{n} a_i = \sum\limits_{(u,v) \in E} (a_u + a_v) -\sum\limits_{i=1}^{n} a_i$

后面一项提出来处理。那么再看$subtask3$,就直接最大生成树就完事了。

从大到小枚举权值然后对于每种权值,再枚举补集的子集连边,并查集写一写就好。就可以做到$O(3^n)$

在边数很多的时候比较优秀的最大生成树做法我知道的貌似也只有Borůvka了。(我才知道这个$u$上面有个圈,貌似是捷克语,蓝莓的意思,谷歌的别问我为啥)

$Boruvka$的问题在于在$log$轮的每一轮中都要找到与其相连的不在同一个已有连通块里的最大边。也就是我们要找到不同集合中的最大的无交的值。

我最开始想的是$trie$然而发现并不会做。然后题解也看不懂说什么$SOS\ DP$(是$sum\ of\ subset$,其实和我意思差不多)

但是因为这道题的值域并不是特别大所以其实可以暴力一点做个值域级别的子集$dp$

每次可以连边的点一定是补集的子集。我们对于每个集合维护其子集中 最大权,最大权所在连通块,和最大权不在同一个连通块里的剩余的最大权。

维护这些信息就足以找到不同连通块的最大边权了。$dp$一轮是$O(18 \times 2^{18})$的,所以总复杂度也就是$O(18 \times 2^{18} \times log \ n)$

 

$1034D:$

大意:数轴上有若干线段$[L_i,R_i]$。你要选定$k$个互不相同的区间$[l_i,r_i]$,最大化$\sum\limits_{i=1}^{k} length(\cup\limits_{j=l_i}^{r_i} [L_j,R_j])$。就是线段并集的大小的和。

(为了方便思考,这里不给子任务而是给子问题了)

$n \le 2\times 10^5,,R_i \le 10^9,k \le \frac{n(n-1)}{2}$

1,离线多次询问,快速查询一个区间的权值。(也就是查询线段并的长度,且根号过不去)

2,快速找到第$k$大的区间的权值。

3,原问题。

提示:不要想什么高级数据结构。稍微暴力一点。

 

1,我都明着说是离线了,还卡你根号。那基本就是扫描线了。我们每次右移右端点并维护对应左端点的答案。

其实是个类似$ODT$的思想,值域很大,但是很多时候相邻的两个值是等价的。

具体来说,我们可以维护一个标记,表示覆盖这个位置的区间中,最大的编号。

然后我们要查询的就是:对于当前右端点$r$,标记值$\ge l$的位置的长度和。

每次右移右端点我们会添加一个区间$[l,r]$。对于所有$\ge l$的点把它们的标记更新为$r$。

这样在$ODT$里就是删除了最近的若干段然后每次只加入一个段。均摊一下总复杂度当然是$O(n\ log\ n)$的。

具体而言,就是说当我们把$k$个位置的标记更新为$r$的时候,如果原标记是$r_0$,那么对于所有$l \in (r_0 ,r]$的询问都会被贡献$k$的答案。

所以这样扫一遍我们就可以在任意时刻得到需要的区间的权值,单次查询$O(log\ n)$,维护的复杂度是$O(n\ log\ n)$

2,第$k$大,这种看起来麻麻烦烦的题里看着很像二分答案。我们考虑二分出一个权值$x$然后找到比它权值要大的区间有多少个。

还是要用到上面说的方法,但是要在上面的操作中预处理出来一个:对于每个右端点,维护出相较于上一个右端点,新增的段和段的长度。(存在$vector$里)

然后同时维护一个左端点$l$表示所有$[1,r],[2,r]...[l,r]$的权值都$\ge mid$。$[l+1,r]$就不满足。

因为如果两个区间相包含,那么大的区间的呃权值一定不比小的区间的权值小。也就是如果你选择了区间$[l,r]$那么一定也会选择$[l,r+1]$

所以说,随着右端点的增加,这个左端点也是单调不降的。用一个单调指针,每一次都去尝试右移这个左端点指针。

维护一些变量分别表示当前区间的权值以及一个数组表示以这个点为左端点的总贡献(加入时维护,右移左端点时会用到)

每次只考虑那个$vector$里的增量就可以了,总个数是$O(n)$的。所以二分答案的总复杂度也是$O(nlogn)$的。

其实我觉得在二分答案里直接做那个$ODT$一样的东西,$O(nlog^2n)$会比这个简单得多吧。

3,二分的过程中统计一下$\ge mid$的区间的权值和就行了。还需要记录一下个数,如果超出$k$就要减掉$(cnt-k)\times mid$

 

 

$1276E:$

大意:你有$4$块石头,最开始分别在$a_1,a_2,a_3,a_4$(可能重复)。每次操作可以选定两个石头$x,y$把$x$移动到$2y-x$(也就是$x$关于$y$对称的位置)

要求最后所有石头的坐标是$b_1,b_2,b_3,b_4$(没有顺序,不要求一一对应。即不要求$a_1\rightarrow b_1$之类的)

构造方案。$-10^9 \le a_i,b_i \le 10^9$。操作步数不能超过$10^3$。无解输出$-1$

$subtask1:$保证$a_1=a_2=0,a_3=a_4=1,b_1=b_2=x,b_3=b_4=x+1$

$subtask2:$保证$a_1=0,gcd(a_2,a_3,a_4)=1$。$b_1=b_2=0,b_3=b_4=1$

$subtask3:$保证所有$a,b$在模$G$之后同余为$R$。$b_1=b_2=R,b_3=b_4=G+R$。其中$G=gcd(a_2-a_1,a_3-a_1,a_4-a_1)$

$subtask4:$保证$\forall i,a_i \equiv 37(\mod 233),b_i \equiv 55(\mod 233)$

$subtask5:$保证$b_1=0,gcd(b_2,b_3,b_4)=1$。$a_1=a_2=0,a_3=a_4=1$

$subtask6:$无特殊限制

 

看看$subtask3$与$subtask2$的区别。$G=gcd(a_2-a_1,a_3-a_1,a_4-a_1)$。

根据对称的性质,我们不管怎么移动,最终石子的落点$x$也一定在模$G$意义下为$R$

所以只要把所有坐标都减去$R$之后再除$G$。这样就保证了所有石子的$gcd=1$。

同时一定既有奇数又有偶数(全偶$gcd\neq 1$,全奇整体减后$gcd\neq 1$)

如果$G$或者$R$使得$a,b$不能两两匹配,那么就无解。

所以。。。$subtask4$就是一档$puts("-1")$的部分分(都说了我是瞎出的)

首先先假如我们会$subtask1$然后再去看$subtask2$。

其实是一模一样的对吧?因为你的操作是可逆的(对称而已),所以$subtask1$是让石子扎堆,$subtask2$是让扎堆的石子分散。

那么你只要构造一个让$b$扎堆的方案,然后进行其逆过程即可。

$subtask5$是在干什么?将扎堆的石子平移。

假如我们能做到,那么考虑我们的全过程:把石子扎堆到某个位置,然后平移到另一个位置,然后再逆扎堆。

最后一步和第一步一样不再考虑,所以问题只是怎么扎堆,怎么平移堆。

所谓扎堆,就是要把所有石子的最大距离改成$1$。设每个时刻石子是有序的,即$a_1 \le a_2 \le a_3 \le a_4$。同时设最大距离$d=a_4-a_1$

扎堆的话肯定就是把两侧的石子往中间折来减小最大距离,那么可以想到:

如果$\frac{d}{2} \ge a_i-a_1 \ge \frac{d}{4}$那么我们就可以让$1$沿着$i$翻折从而使$d$减小$\frac{1}{4}$。如此操作$a_4$也是同理的。如果一直这么操作复杂度就是$O(log)$的。

问题就在于如果并没有石子在$a_1+\frac{d}{4} \le  x \le a_4-\frac{d}{4}$的范围内怎么办。

设$d_i$表示石头$i$距离$a_1,a_4$中较近的那一个的距离,一定不可能$d_2=d_3=0$否则要么已经有$d=1$要么$gcd\neq 1$

我们考虑对同一个石子连续两次的操作$i$连续分别于$j,k$为对称中心跳一下,那么最后所在的位置就是$a_j+a_k-2a_i$。通过这种方法我们可以使$d_i+=2d_j$。

经过$log$次就可以使得$d$到足够大就可以继续进行上面的步骤了。至此扎堆过程完成了。

现在要考虑把石子堆平移,从$[x,x+1]$到$[x+k,x+k+1]$。我们假定$k=2c$。是偶数。如果是奇数的话就先草率的平移一位就好了。

假如我们要往右移动移动的话,那肯定要跟最右边的石子做对称操作,把剩下三个石子都沿着最右边的石子翻转,那么石子所在的区间就向右了$d$

但是如果$d$很小的话复杂度肯定是不对的,所以我们要适度扩大$d$.。

我们定义一次扩展是指将某一个石子沿着最左边和最右边的石子各操作一次。这样$log$次扩展就可以使$d>c$

接下来每一轮操作,我们先对石子堆进行一次逆扩展(只要石子的相对位置不变,上面那个扩展操作显然是可逆的)

逆扩展一次之后,当前的$d<c$那么就可以执行两次上面说的平移操作,这样会使$c$下降$d$。

类似一个倍增从大往小跳的过程。到最后就有$d=1,c=0$了。复杂度也是$O(log)$级别的

其实代码写起来还是挺愉悦的。

 

posted @ 2020-06-09 18:27  DeepinC  阅读(271)  评论(0编辑  收藏  举报