2020牛客暑期多校训练营(第四场)题解

部分题题解,剩余部分待补
A:Ancient Distance
这道题写了个根号的复杂度的,没有被卡
我们只需要做到O(1)判断就好了!
然后我们可以写一个有关于单调性的解法,然后我们可以类似于整除分块可能更好理解!
nlogn的代码和思路以后会补,先放上根号的代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+5;
struct Edge{int to,nxt;}e[N];
int fst[N],tote,n,dep[N],a[N],ma,fa[N],ans[N],id[N],dfc;
void adde(int u,int v){e[++tote]=(Edge){v,fst[u]};fst[u]=tote;}
void dfs(int u){
    id[++dfc]=u;ma=max(ma,dep[u]);
    for(int i=fst[u],v;i;i=e[i].nxt)v=e[i].to,dep[v]=dep[u]+1,dfs(v);
}
void solve(int L,int R,int l,int r){
    if(L>R||l>r)return;
    if(l==r){for(int i=L;i<=R;i++)ans[i]=l;return;}
    int mid=(L+R)>>1;ans[mid]=0;
    for(int i=1;i<=n;i++)a[i]=dep[i];
    for(int i=n,x;i>=1;i--){
        x=id[i];
        if(x==1||a[x]==dep[x]+mid)a[x]=-1,ans[mid]++;
        a[fa[x]]=max(a[fa[x]],a[x]);
    }
    solve(L,mid-1,ans[mid],r);solve(mid+1,R,l,ans[mid]);
}
int main(){
    while(scanf("%d",&n)!=EOF){
        for(int i=1;i<=n;i++)fst[i]=0;tote=ma=dfc=0;
        for(int i=2;i<=n;i++)scanf("%d",&fa[i]),adde(fa[i],i);
        dfs(1);solve(0,ma,1,n);
        LL res=0;
        for(int i=1;i<=ma;i++)res+=(LL)i*(ans[i-1]-ans[i]);
        printf("%lld\n",res);
    }
    return 0;
}

B: Basic Gcd Problem
这题算是这场的签到题了吧,我们只需要转化下,然后发现可以通过算一个数因数的次幂和,然后我们要O(logn)完成这个操作,线性筛一波就好了!
C: Count New String
这题我们要注意首先一个转化,就是把最外围的那个函数拆掉,然后直接统计不同子串个数扫一遍就好了
这是一个重要的思路,然后我们考虑这个字符集是10是一个隐士条件
这意识着我们不同的子串会很多,但是大部分都是一段很长的区间都是一个字母这样的条件很多
然后我们可以的得出一个基于字符集的hash做法,分别考虑每一个字母出现了多少次
但是这个hash用了unordered_map以后还是会tle
于是换成了广义sam,注意这里的广义sam我们不是每次把节点重***们采用记录一下上次的节点,由于已经插入过了,我们直接把这个节点接着记录的节点插一下就好了,可能写法还需要维护一个trie,然后我们bfs来完成这个过程!
这题感觉可能首先还是和普通的广义sam有一定的区别,是一种新的方式,我们可以拓宽一下
然后就是字符集小的情况我们可以想想奇怪的处理方法来通过字符集降低复杂度!

#include<bits/stdc++.h>
#define LL long long
#define pb push_back
using namespace std;
const int N=2e5+5;
char str[N];int n,st[N],tp,now,nd[N];LL ans;
struct SAM{
    int ch[N*10][10],slink[N*10],len[N*10],tot,lst,Root;
    int newnode(int l){len[tot]=l;return tot++;}
    void init(){lst=Root=newnode(0);}
    int extend(int p,int c){
        //printf("extend:%d %d\n",p,c);
        int np=newnode(len[p]+1);
        while(!ch[p][c])ch[p][c]=np,p=slink[p];
        if(p==Root&&ch[p][c]==np)slink[np]=Root;
        else{
            int q=ch[p][c];
            if(len[q]==len[p]+1)slink[np]=q;
            else{
                int nq=newnode(len[p]+1);
                slink[nq]=slink[q];slink[q]=slink[np]=nq;
                memcpy(ch[nq],ch[q],sizeof(ch[nq]));
                while(ch[p][c]==q)ch[p][c]=nq,p=slink[p];
            }
        }
        return np;
    }
}sam;
struct Trie{
    int ch[N*10][10],tot,sd[N*10];
    int insert(int u,int c){return (!ch[u][c]?ch[u][c]=++tot:ch[u][c]);}
    void build(){
        queue<int>que;while(!que.empty())que.pop();
        sam.init();que.push(0);
        while(!que.empty()){
            int u=que.front();que.pop();
            for(int i=0;i<10;i++)if(ch[u][i])
                que.push(ch[u][i]),sd[ch[u][i]]=sam.extend(sd[u],i);
        }
    }
}trie;
int main(){
    scanf("%s",str+1);n=strlen(str+1);
    for(int i=n;i>=1;i--){
        if(!tp||str[i]<=str[st[tp]]){now=trie.insert(now,str[i]-'a');st[++tp]=i;nd[tp]=now;continue;}
        while(tp&&str[i]>str[st[tp]])tp--;
        for(int j=tp+1;j<=n-i+1;j++){
            str[n-j+1]=str[i];now=trie.insert(nd[tp],str[n-j+1]-'a');
            st[++tp]=n-j+1;nd[tp]=now;
        }
    }
    trie.build();
    for(int i=1;i<sam.tot;i++)ans+=(LL)sam.len[i]-sam.len[sam.slink[i]];
    printf("%lld\n",ans);
    return 0;
}

D: Dividing Strings
这题感觉就是思路就是枚举长度,然后log判断,结果细节一大堆...
我们可以预处理出哈希的值,然后我们哈希判断可能更方便!
代码还没写完,待补见谅!
F: Finding the Order
类似于四边形不等式判断就好了,其实也就多画画图就可以猜个结论!
也可以设一个值然后来大力特判...大家可以自行选择方法!
注意画图的重要性!
H: Harder Gcd Problem
这题考试的时候用了一个奇怪的结论过题额,还是一个有关于sort的排序,然后关键字大小比较尤为繁琐!
用一个堆来判断然后不断细节特判两两匹配...
然后我们这题的妙处有一个结论是是去掉大于n/2的质数和1,然后剩下都可以两两匹配
然后我们从大到小枚举这个质数,然后发现这个质数的倍数尚未匹配的个数是多少?
我们分两类讨论:
如果是奇数:我们把2*i单独提出来,其他两两匹配就好了!
否则直接两两匹配就好了,因为我们注意到2是一个奇怪的数,可以当做gcd这方便了我们的构造,这样就可以很方便的构造出一组解了,需要注意的是我们可以用vector和一个pair<int,int>这样可能能好实现一些!
这题启发大概是我们首先需要搞出一个结论,就是要给出答案,我们要先求出这个组数通常能让我们更好地得出方案!然后就是我们配对的最后策略通常是倒序,然后这样有一个问题就是我们要把难以匹配的先搞,然后在匹配其他的,还有一个这里有关于这个2的思路甚为精妙
代码:

#include<bits/stdc++.h>
#define LL long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int N=2e5+5;
int pri[N],len;bool np[N],usd[N];
void init(){
    for(int i=2;i<N;i++){
        if(!np[i])pri[++len]=i;
        for(int j=1;j<=len&&i*pri[j]<N;j++){
            np[i*pri[j]]=true;
            if(i%pri[j]==0)break;
        }
    }
}
int main(){
    init();int T;scanf("%d",&T);
    while(T--){
        int n;scanf("%d",&n);usd[1]=true;
        for(int i=2;i<=n;i++)if(i<=n/2||np[i])usd[i]=false;else usd[i]=true;
        vector<pii>ans;ans.clear();
        for(int i=(n>>1),res,lst;i>=2;i--)if(!np[i]){
            res=lst=0;for(int j=i;j<=n;j+=i)if(!usd[j])res++;
            if(res&1){
                if(!usd[i])usd[i]=true,lst=i;
                for(int j=3*i;j<=n;j+=i)if(!usd[j]){
                    usd[j]=true;
                    if(lst)ans.pb(pii(lst,j)),lst=0;else lst=j;
                }
            }else{
                for(int j=i;j<=n;j+=i)if(!usd[j]){
                    usd[j]=true;
                    if(lst)ans.pb(pii(lst,j)),lst=0;else lst=j;
                }
            }
        }
        printf("%d\n",(int)ans.size());
        for(auto x:ans)printf("%d %d\n",x.first,x.second);
    }
    return 0;
}

I: Investigating Legions
这题大概给出了一个随机方式,然后我们可以直接根据这个本机调参就可以了!
这题的思路很多,我们可以随机调整,也可以网络流匹配(这个作者不太懂)
这里最简单的是这个概率做法,我们可以利用三元环的思路构造就可以了,然后本机调参到最优就好了!
这题首先是我们有一个概率算法,而且这个给出的随机方式可以有利于我们本机调参!
代码:

#include<bits/stdc++.h>
#define LL long long
#define pb push_back
using namespace std;
int ans[305];bool e[305][305];char str[90005];
int main(){
    int T;scanf("%d",&T);
    while(T--){
        int n,s,now=0,cc=-1;vector<int>v;
        scanf("%d%d",&n,&s);scanf("%s",str+1);
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++)e[i][j]=e[j][i]=(str[++now]=='1');
            e[i][i]=true;ans[i]=-1;
        }
        for(int i=1;i<=n;i++)if(ans[i]==-1){
            v.clear();cc++;
            for(int j=1;j<=n;j++)if(ans[j]==-1&&e[i][j])v.pb(j);
            for(int j=1,cnt;j<=n;j++)if(ans[j]==-1){
                cnt=0;for(auto x:v)if(e[j][x])cnt++;
                if(cnt>=(v.size()>>1))ans[j]=cc;
            }
        }
        for(int i=1;i<=n;i++)printf("%d%c",ans[i]," \n"[i==n]);
    }
    return 0;
}
全部评论

相关推荐

bg:双非本,一段中小厂6个月测开实习今天发这个帖子主要是想聊一聊我秋招以来的一个发展我是在8月底辞职,打算秋招,可是看网上都说金九银十就想着自己就是一个普通本科生,现在九月份都是一些大神在争抢,所以9月份基本上没投,等到了10月份才开始秋招,可是这个时间好像已经有些晚了,今年秋招开启的格外早,提前到了7,8月份,我十月才开始,官网投了很多公司,没有任何一个面试机会,这个情况一直到了十月底才有了第一个面试,当时没有面试经验,所以不出意外的挂了后续就是漫长的投递,但是毫无例外没有面试,没有办法我只能另辟蹊径开始在BOSS上边投递,然后顺便也根据BOSS上边这个公司名称去浏览器搜索看看有没有官网投递渠道,毕竟官网上投递后还是可以第一时间被HR看到的,然后一直不停投递,一开始第一个星期基本上都是投的正式秋招岗位到了第二个星期才开始实习和正式一起投,到十一月底的时候已经沟通了700➕才有一共1个正式的,5个要提前实习的,3个实习的面试,最后结果是过了1个要提前实习的和2个实习的每次面试我都会复盘,发现这些小公司面试官问的五花八门,有的专问基础,有的专问项目,有的啥都问,不过自己也是看出来了一下门道,就是小公司不像大公司面试官那样能力比较强基本上你简历上边的他都会,然后会根据简历来问,小公司面试官他们更多的是看自己会什么,然后看看你简历上边哪些他也是会的然后来问,经过不断的复盘加上背各种各样面试题,到了11月底12月初才有了1个要提前实习的offer还有2个实习的offer,而且薪资待遇对我来说已经很可观了可是啊,人总是这样得了千钱想万钱,我又开始不满现状,但是此时的我面试能力经过这么多面试和复盘已经很强了,然后在十二月份运气爆棚,被极兔和小鹏补录捞起来面试,还有个百度测开的实习面试,这个时候因为有了offer所以感觉有了底气,面试也很自信,最后结果是全部都过了那个时候我感觉自己真的很厉害,我问了极兔那边的HR像我这样的双非本收到offer的在极兔有多少?他告诉我产研岗90%都是硕士,10%里边基本上都是211,985,想我这样的很少很少,那一刻感觉自己超级牛逼,小鹏就更不用说了,最后也是不出意外选择了小鹏所以我就我个人经历想对和我学历履历差不多的牛友一些建议第一:秋招一定要趁早,真到了9,10月,那个时候可能你投的结果可能还不如7,8,11月,第二:最好先拿小公司实习或者正式练练手,提升一下面试能力,我个人觉得因为小公司问的五花八门所以你会更加横向去提升自己能力,而且大公司其实面试没有那么难,除了一些非常卷的岗位,公司大神比较多会问的很难,一般好点的公司都不会问的那么难,他们也知道都是应届生不会要求那么高第三:当有一定能力后,就是坚持了,对于我们这样的学历,没有特别强的履历情况下,就是要抓住提前批和补录的机会,这个时候各方面不会卡的很严,是我们很好很好的一个机会第四:就是运气也是很重要的一部分,不过这个很难去说什么最后祝各位牛友都能收获自己满意的offer😁😁😁
秋招,不懂就问
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务