BestCoder Round #52 (div.1)

时间:2022-12-18 05:04:03

这周六BC和CF又差点打架,精力不够啊。。。结果打BC没起来,就看了一眼题跑了。。。今天早上补补吧,(因为今天晚上还要打UER= =)

先放官方题解:

1000 Victor and Machine

首先是关于题意的问题,之前有人向我提出题意不清,对于y<w的情况,没有说明该如何处理,但是在咨询了其他几个小伙伴之后,我决定还是不修改题面了,因为题中已经说明了是“机器每次开启的瞬间”,也就是说机器关闭之后w就会清零。我不大清楚是否有人因为这个坑点而被hack或者fst了,如果有的话,对此我只能表示遗憾。

因为数据范围很小,所以我们可以手动模拟,枚举每个小球弹出的时刻,再特判一下机器的关闭情况就可以了。实际处理起来的话或许有些蛋疼,但是,只要细心一点并且耐心一点,都是能够AC的。

另外一种解法就是推公式,我们发现,在机器每次开启的时间,都有x/w+1个小球被弹出(无论w是否整除x),假设这个数目为a,那么每弹出a个小球就需要花费x+y的时间,那么,第n个小球弹出的时刻就是(n-1)/a*(x+y)+(n-1)%a*w。要注意的是,这里需要用(n-1)%a*w,因为题中求的是第n个小球弹出的时刻等于是弹出n-1个小球所花费的时间,然而,只要测试一下样例就能注意到这一点了。

1001 Victor and World

部分人可能对这道题的题面有点熟悉,其实这道题的标题本来叫做Victor and Around the World的,但是因为太长了所以就省略了两个单词。当然,这道题与POI 2014的那道Around the World其实没有任何关系。

我们首先需要预处理出任意两个国家之间的最短距离,因为数据范围很小,所以直接用Floyd就行了。之后,我们用f[S][i]表示访问国家的情况为S,当前最后访问的一个国家是i所需要的最小总油量,其中,S的二进制表示记录了访问国家的情况,S在二进制表示下的第i位(不管是从左往右还是从右往左都可以)如果是1则表示第i个国家被访问过了,否则表示第i个国家没有被访问过,那么f[S|(1<<i)][i]=min(f[S][j]+f[i][j]),其中i和j满足S&(1<<j)=1且S&(1<<i)=0。最开始时,除了f[1][1]是0,其他情况都是无穷大,之后先枚举S,再枚举i(我验题的时候因为这里搞反结果WA了),那么最终的答案就是min(f[(1<<n)-1][i]+f[i][1]),其中i\in∈[2,n]。总复杂度为O(n^3+n^2*2^n)O(n​3​​+n​2​​∗2​n​​)。

1002 Victor and Toys

首先分母就是C(m,3)C(m,3),考虑如何计算分子。

注意到期望的独立性,我们可以首先用O(n+m)O(n+m)的时间利用差分前缀和预处理出每个点被几个区间覆盖,假设第ii个点被s_is​i​​个区间所覆盖,那么第ii个点对分子的贡献即为w_i\times C(s_i,3)w​i​​×C(s​i​​,3),注意不要爆long long。

1003 Victor and Proposition

这是一道图论题。其中,命题与命题之间的关系我们可以看成边,如果A命题是B命题的充分条件,那么我们就可以从A向B连一条有向边,同理,如果B是A的充分条件,那么就从B向A连一条有向边。那么如果两个命题i,j互为充要条件,那么i和j就会属于同一个强连通分量,所以,这道题就变成了求强连通分量的题目。强连通分量有两种求法,分别是Kosaraju算法和Tarjan算法,在这里我就不详细介绍了。这道题的关键实际上在于建图。

首先,题中给出了一个以1号结点为根的树形结构,我们在构图的过程中,要将结点i和以xi为根的子树中与xi深度差不超过di的结点连一条有向边。但是,结点最多有十万个,如果直接连的话肯定是会超时的,所以需要一些黑科技进行优化。我们要用线段树来优化建图。对于每个结点,我们都需要用一棵线段树来维护其子树中深度为i的结点有哪些,那么,连边的时候,我们只需要将结点i往xi的线段树中,dep[x_i]到dep[x_i]+d_i的范围内的所有结点连一条边即可,既然已经建了线段树,如果线段树上每个结点往其子节点都连一条边,那么我们只要将结点i与对应线段树中[dep[x_i],dep[x_i]+d_i]范围内的结点连一条边。但是这样一来,建图的速度反而变慢了,没有关系,我们可以进行线段树合并。首先,遍历整个树,对于树上的叶子结点,我们都建立一棵线段树,之后,在返回的过程中,每个结点对应的线段树都是其所有子结点线段树合并的结果,这样一来,建图的复杂度就变成O(n\log n)O(nlogn)了。

1004 Victor and String

这道题是这场比赛一开始就定下的一道题,虽然感觉AC人数可能不是很多,但VictorWonder还是毅然决然地拿了出来。

这道题的题意是简单的,而且,我相信没有操作一的话,有一部分人应该会做。操作三实际上就是查询有多少本质不同的回文串,在添加字符的时候用manacher处理一下就差不多了,操作四的话可能要麻烦一点,打个后缀自动机应该就差不多了(本人也没有试验过,所以如果不行的话就当我口胡好了)。但是,我们还有另外一种选择,那就是回文自动机(我更习惯叫它回文树)。在这里,我假设大家都了解回文自动机的构建方法(如有不懂请自行百度),那么本质不同的回文串数目就是回文自动机上结点的总数-2,因为初始化的时候初始化了0结点和-1结点。至于询问四,我们则需要额外维护一个变量depth,对于一个结点i,depth[i]表示的是,沿着结点i的后缀链走到0结点的距离,那么每次新增加一个字符,假设此时代表最长回文后缀的结点为x,那么答案就加上depth[x]。

然而,现在比较麻烦的是,多了向字符串的前端增加一个字符的操作,但实际上,并没有加大多少难度。

我们首先定义后缀结点是代表当前字符串最长回文后缀的那个结点,前缀结点是代表当前字符串最长回文前缀的结点,同理,我们定义一个结点x的后缀链指向的是结点x所代表回文串的最长回文后缀,定义结点x的前缀链指向的是结点x所代表回文串的最长回文前缀。往当前字符串的后端增加一个字符,我们只需要沿着后缀结点的后缀链走下去寻找到符合要求的位置再进行更新即可。那么,往当前字符串的前端增加一个字符也是类似,我们只要沿着前缀结点的前缀链走下去就行了。

在这个时候,或许有人已经发现了一个细节,那就是前缀链是完全不需要的,因为回文自动机上每个结点所代表的都是回文串,那么其最长回文前缀和最长回文后缀都是相同的,所以一个结点前缀链指向的结点和后缀链指向的结点是相同的,我们完全不需要前缀链,只需要记录前缀结点和后缀结点就行了。需要注意的是,如果新增加一个字符之后,使得当前串就是回文串,那么需要同时更新前缀结点和后缀结点。

另外,在新增字符的时候,我们需要沿着后缀链寻找符合要求的位置,在这个过程中,我们需要在当前字符串中进行查找,因此,我们必须要记录下整个字符串,我的做法是用一个长度为2n的序列进行储存,以n这个位置为起始位置,用L和R来记录字符串两端所处的下标,那么处理起来就方便了。

最后,虽然看起来好像有些繁琐,实际上在经过这样的扩展之后,回文自动机的复杂度并没有改变。往前加字符和往后加字符都需要写特定的函数,但只要复制粘贴再修改一下就行了。

一些坑点:pretest是随机的小数据,而最终的数据都是人工构造的大数据,构造方法是将一个回文串翻倍再翻倍。还有一个点是全部都是a的,所有回文串的数目是超出int范围的,需要注意。

--------------------------------

A是一道沙茶模拟懒得写了,B这个状压挺简单写一发,结果WA了N发。。。

1WA:输出忘回车了

2WA:inf好像开小了。。。?

3WA:忘特判n==1的情况

4WA:重边没考虑,邻接表用习惯了。。。

5WA:特判输出0忘回车了。。。

6WA:很严重的错误。。。1<<i-1 总是写成 1<<i。。。要引起注意。。。

。。。终于结束了。。。写了10分钟,调了一个小时。。。代码能力喂狗。。。

 #include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
#include<cstring>
#define PAU putchar(' ')
#define ENT putchar('\n')
#define MSE(a,b) memset(a,b,sizeof(a))
#define REN(x) for(ted*e=fch[x];e;e=e->nxt)
#define TIL(x) for(int i=1;i<=x;i++)
#define ALL(x) for(int j=1;j<=x;j++)
using namespace std;
const int maxn=,maxs=(<<)+,inf=1e9;
inline int read(){
int x=;bool sig=true;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')sig=false;
for(;isdigit(ch);ch=getchar())x=*x+ch-'';return sig?x:-x;
}
inline void write(int x){
if(x==){putchar('');return;}if(x<)putchar('-'),x=-x;
int len=;static int buf[];while(x)buf[len++]=x%,x/=;
for(int i=len-;i>=;i--)putchar(buf[i]+'');return;
}
int T,n,m,d[maxn][maxn],f[maxs][maxn],lim;
int main(){
T=read();
while(T--){
n=read();m=read();lim=<<n;int x,y;
TIL(n)ALL(n)d[i][j]=inf;TIL(n)d[i][i]=;
TIL(m)x=read(),y=read(),d[x][y]=d[y][x]=min(d[x][y],read());
if(n==){write();ENT;continue;}
for(int k=;k<=n;k++)TIL(n)ALL(n)d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
for(int S=;S<lim;S++)TIL(n)f[S][i]=inf;
f[][]=;
for(int S=;S<lim;S++)TIL(n)ALL(n)if(!(S&(<<i-))&&(S&(<<j-)))
f[S|(<<i-)][i]=min(f[S|(<<i-)][i],f[S][j]+d[i][j]);
int ans=inf;
for(int i=;i<=n;i++)ans=min(ans,f[lim-][i]+d[i][]);write(ans);ENT;
}
return ;
}

其他的题不可做啊。。。。= =