2019年华南理工大学程序设计竞赛(春季赛)

传送门

一下题解均是大概思路

Problem A

这个题0/10/1写出来仅仅是带偏你的思路,本质上这题就是:给你88种数字,拼凑,使得他们的乘积和在区间[L,R][L,R]范围之内。

考虑爆搜,不断的对每种数进行搜索,如果发现当前数的乘积在[L,R][L,R]范围内,则直接累计答案,而倘若当前的乘积大于RR则证明之后的所有情况均不可能,故直接剪掉即可。

事实证明,这样的爆搜非常的快!


Problem B


Problem C

考虑贪心。我们优先考虑一个管的情况。

要使得字典序最大,一种显然的方法就是,先取两边最大的。而如果两边l,rl,r的大小相同,则我们直接向中间收缩,直到找到两边大小不相同的的位置l,rl',r'。此时我们只需要优先选取较大的位置即可。而这个过程可以用双指针来维护。

最后我们可以对两个管分开处理,分别求出两个管的最优情况,最后用00将这两个种最优解连接,再做一次上述过程即为最终答案。


Problem D


Problem E

原题+1+1

poj2676poj2676


Problem F


Problem G


Problem H

原题+2+2

hdu5381hdu 5381

在这题中甚至没有查询,连分块都不用了。

然后没有做出来


Problem I

原题+3+3

2018 牛客网暑期ACM多校训练营(第二场)D题。

原封不动的原题,只需要知道在最低点买入,最高点卖出即可。


Problem J


Problem K

原题+4+4

poj3415poj 3415的弱化版,只需要将那题的kk改为11即可通过

分析:

我们发现,在这个题中,询问非常多,字符串的长度并不是非常的大。因此我们可以考虑先把答案预处理出来。

因为涉及两个串的子串个数问题,因此我们不妨考虑使用后缀自动机预处理。

我们对其中一个串str1str_1建立后缀自动机,我们用拓扑排序自底向上的对整个自动机的endposendpos集合的大小numnum进行更新(因为对于某个结点pp,他沿着后缀连接走到的结点fa(p)fa(p)必然是结点pp的后缀,故结点pp的答案必定要更新到结点fa(p)fa(p))。

更新完endposendpos集合的大小numnum后,我们需要维护一个前缀和sum(p)sum(p),代表到达当前结点pp时,终止点在endposendpos的所有子串中,在串str1str_1中出现的次数。

完成上述工作之后,我们只需要把str2str_2串丢到后缀自动机上去匹配,每次去统计答案并记录下来供tt次询问查询即可。

整体时间复杂度:O(len2)mathcal{O}(len^2)

代码:
#include <bits/stdc++.h>
#define maxn 4005
using namespace std;
char str[maxn];
typedef unsigned long long ll;
struct SAM{
    int next[maxn*2][26],fa[maxn*2],len[maxn*2],last,cnt=0;
    ll cntA[maxn*2],A[maxn*2];
    ll num[maxn*2],sum[maxn*2];
    SAM(){clear();}
    void clear(){
        last=cnt=1;
        memset(sum,0,sizeof(sum));
        memset(num,0,sizeof(num));
        memset(next[1],0,sizeof(next[1]));
        memset(len,0,sizeof(len));
        fa[1]=len[1]=0;
    }
    void Insert(int c){
        int p=last;
        int np=++cnt;
        num[np]=1;
        memset(next[cnt],0,sizeof(next[cnt]));
        len[np]=len[p]+1;last=np;
        while(p&&!next[p][c]) next[p][c]=np,p=fa[p];
        if(!p) fa[np]=1;
        else{
            int q=next[p][c];
            if(len[q]==len[p]+1) fa[np]=q;
            else{
                int nq=++cnt;
                len[nq]=len[p]+1;
                memcpy(next[nq],next[q],sizeof next[nq]);
                fa[nq]=fa[q]; fa[np]=fa[q]=nq;
                while(next[p][c]==q) next[p][c]=nq,p=fa[p];
            }
        }
    }
    void build(char *s){
        memset(cntA,0,sizeof(cntA));
        memset(A,0,sizeof(A));
        for(int i=1;i<=cnt;i++) cntA[len[i]]++;
        for(int i=1;i<=cnt;i++) cntA[i]+=cntA[i-1];
        for(int i=cnt;i;i--) A[cntA[len[i]]--] =i;
        int tmp=1;
        int sz=strlen(s);
        for(int i=0;i<sz;i++){
            num[tmp=next[tmp][s[i]-'a']]=1;
        }
        for(int i=cnt;i;i--){
            int x=A[i];
            num[fa[x]]+=num[x];
        }
        for(int i=1;i<=cnt;i++){
            int x=A[i];
            sum[x]=sum[fa[x]]+num[x]*(len[x]-len[fa[x]]);
        }
    }
    int query(char *s){
        int sz=strlen(s),now=1,Len=0;
        int res=0;
        for(int i=0;i<sz;i++){
            int c=s[i]-'a';
            if(next[now][c]){
                Len++;
                now=next[now][c];
            }
            else{
                while(now&&!next[now][c]) now=fa[now];
                if(now){
                    Len=len[now]+1;
                    now=next[now][c];
                }
                else{
                    Len=0,now=1;
                }
            }
            res+=sum[fa[now]]+num[now]*(Len-len[fa[now]]);
        }
        return res;
    }
}sam;
ll ans[maxn];
char tmp1[maxn];
char tmp2[maxn];
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%s",str);
    int t;
    scanf("%d",&t);
    int len=strlen(str);
    for(int i=0;i<len-1;i++){
        memset(tmp1,0,sizeof(tmp1));
        memset(tmp2,0,sizeof(tmp2));
        for(int j=0;j<=i;j++) tmp1[j]=str[j];
        for(int j=i+1;j<len;j++) tmp2[j-i-1]=str[j];
        sam.clear();
        int sz=strlen(tmp1);
        for(int i=0;i<sz;i++){
            sam.Insert(tmp1[i]-'a');
        }
        sam.build(tmp1);
        ans[i+1]=sam.query(tmp2);
    }
    while(t--){
        int n;
        scanf("%d",&n);
        printf("%lld
",ans[n]);
    }
    return 0;
}


Problem L

温暖的签到题+1


总结:

状态和节奏都异常稀烂的一场比赛。

全场梦游。开场看到隔壁黄色气球打得异常的多,因此上机直接打找到水题两分钟1y(这里有个小插曲,打完代码之后发现还没登陆,又花了一些时间登陆,再又花了一些时间找如何提交,痛失1血,可能这也是全场比赛的缩影)。

过了L题之后,我全场看题,发现了E是个裸的dancing links,准备掏出板子一端狂抄。此时发现I题已经有不少人A了,看题之后发现这是个多校曾经出过的套路题,我把这题喂给qym之后也成功在20mins 1y。此时看榜,发现E题已经有人过了,C题也已经有人正在尝试,于是我让队友思考C题,我自己独自抄E题的板子。而谁知道,至此就走上了自闭的道路一去而不复返。

大概20min后,我把E题的板子抄完了,但是奈何对原题不是很熟悉,不知道如何读入,试了好多次都无法成功得出正确答案。此时看到C题过题数飙升,队友们也得出了先做一个串,再做另外一个串最后两串合并的算法。因此队友下机,我……因为比赛没有打印系统,我只能对着模板干瞪眼……

过了一段时间之后,qym成功完成了他的算法,交上去1wa。此时已经过去了差不多一个半小时了,看到榜上一堆人3题4题,突然有些慌,再加上连续把IE卡在手上,突然自闭之感油然而生,仿佛梦回去年上半学期……

于是我和lqs对着那100多行的dancing link逐行比较,最后发现N处地方抄错,1个范围写错(orz),最后改了改也成功1y了。

过了E之后,我看了下C题,看完之后口胡出了一个贪心的方法,大家都没意见之后我直接上机,写了一会之后发现 2wa。静下心来debug之后,发现,要初始化没初始化,要塞0的没有塞0。(深度背锅……),把这些弱智错误改完之后才成功3y。

此时已经过去了3个小时了……(然后其实比赛已经结束了)看到榜上AFH都有人开,随跟榜开题。

lqs看到H题之后,发现这题是他和我都做过的某年某个多校的原题的简化版,随开始解释算法思路。但是此时qym说出了另一个很正确的算法。而在我印象中,好像也曾经用这个思路解决了另一个类似的问题,随支开lqs,让qym上机。

但是写完之后很快 1wa,为了避免崩盘(其实已经崩盘了),我去开了A题,发现涉及0/1数位以及数位乘积,想都没想就喂给qym说是数位dp……qym想了一会未果,就又去肝H题了。结果H换了一种又一种的写法还是不断的2wa,3wa……至此陷入无限的自闭之中。

最后半小时,放弃治疗,把H丢了,三个人再开A和F。静下心来看了下A,貌似0/1的数位对答案没有影响……因此这题貌似不是个数位dp,而是对这2-9这8种数分别填到8位的数上……思考过爆搜,但是感觉时间会爆炸,遂没敢上机。而正当F题的思路大概也有了雏形的时候,比赛结束……比赛确实从3小时的时候就结束了。

赛后问了下其他人,H就是个超级大原题,F题的思路就差最后一步,A题直接爆搜即可。

感觉到了下学期之后,三个人的状态和做题感觉越来越差了……换作我们寒假camp的时候,甚至是去年打ec的那段时候,这套题至少能做出6-7个题目,但是过了四五个月之后状态却变得如此稀烂。做一道题卡一道题。这只能证明这四个月时间里面我们的态度太过与懈怠,以至于我们一直在原地踏步(甚至倒退),而大家都在稳步提高。我觉得之后能做的,只能是加大训练量,端正态度,抛弃一些有的没的杂七杂八的侥幸心态了。

原文地址:https://www.cnblogs.com/Chen-Jr/p/11007146.html