【摸鱼on牛客】2020ICPC 小米 网络选拔赛第一场

2020ICPC 小米 网络选拔赛第一场

前言

前面三个多小时只有两个人在肝题,太艰难了QAQ
好在还是摸进决赛了,and充电宝++(* ̄︶ ̄)

训练情况


训练时出现的问题

前期的问题还是签到的速度,从10min+才开始有过题,跟第一梯队还是有一定差距。。
中期很顺利,几乎没有罚时。。
后期在开构造题G的时候没有想清楚就交了。还是抱有侥幸心理啊。。。然后罚时就炸了。后面是真的慌了,连个最基础的线段树都出了好多错。
以后写题还是得想清楚再交,暴力的算法最多交一发试试水。。

部分题解

G Tree Projection

链接

Tree Projection

题意

给定一颗(n)个点的无根树的某个拓扑序A和DFS序B,要求求出一颗符合的树。(1leq n leq 2 imes 10^5)

题解

(A_1)(B_1)相等,那么直接让其余所有点连向(A_1),显然这是合理的。
然后考虑不相等的情况,这个时候我们先找到(B_1)(A)中的位置,然后在这之前的(A)中的数显然在(B_1)为根的树种在同一颗子树中,我们可以在(B)中找到这颗子树占据的范围,然后根据这范围我们能将问题分成两个部分:
1.一部分有(B_1),这部分还包含不在那颗子树内的其他点,找到这些点在A中的位置后,得到新的子问题,这个问题中符合(A_1=B_1)
2.这部分只包含之前分出来的子树,重复上面的操作递归即可。
按照上面的操作,我们发现这个问题是一定有解的。
我刚开想到这些后直接写,然后T飞了。。
如何在保证复杂度的情况下完成上面的操作?
只需要开一颗线段树维护一下前(i)个A中的点在B中所需的范围,在删B中的点时支持单点修改即可。
在B中删点时将指针从两端往中间扫,只扫到要删的点,这样每个点只扫到删掉一次,就能(O(nlogn))了。

(Code)

#include<bits/stdc++.h>
#define LL long long
#define LD double
using namespace std;
const double PI=acos(-1);
const int INF=1e9;
const int N=2e5+10;
int n;
int cnt=0;
int u[N],v[N];
int w[N];
int a[N],b[N],t[N],byc[N],kcz[N];
struct node{
    int l,r;
    int mx,mn;
}d[N<<2];
#define ls i<<1
#define rs i<<1|1
void build(int l,int r,int i){
    d[i].l=l;d[i].r=r;
    if(l==r){
        d[i].mn=byc[l];
        d[i].mx=byc[l];
        return;
    }
    int mid=l+r>>1;
    build(l,mid,ls);
    build(mid+1,r,rs);
    d[i].mn=min(d[ls].mn,d[rs].mn);
    d[i].mx=max(d[ls].mx,d[rs].mx);
}
void C(int i,int r){
    if(d[i].l==d[i].r){
        d[i].mx=-1;
        d[i].mn=n+1;
        return;
    }
    if(r<=d[ls].r) C(ls,r);
    else C(rs,r);
    d[i].mn=min(d[ls].mn,d[rs].mn);
    d[i].mx=max(d[ls].mx,d[rs].mx);
}
int MN,MX;
void ask(int i,int r){
    if(d[i].l==d[i].r){
        MN=min(d[i].mn,MN);
        MX=max(d[i].mx,MX);
        return;
    }
    if(r<=d[ls].r) ask(ls,r);
    else {
        MN=min(MN,d[ls].mn);
        MX=max(MX,d[ls].mx);
        ask(rs,r);
    }
    return;
}
void get(int l,int r){
    if(l>=r) return;
    if(l+1==r){
        ++cnt;
        u[cnt]=b[l];
        v[cnt]=b[r];
        return;
    }
    if(a[1]==b[l]){
        for(int i=l+1;i<=r;++i){
            ++cnt;u[cnt]=b[l];v[cnt]=b[i];
        }
        return;
    }
    int x=kcz[b[l]];
    MN=r+1;MX=l-1;
    ask(1,x-1);
    for(int i=l;i<MN;++i){
        if(i>l){
            ++cnt;u[cnt]=b[l];v[cnt]=b[i];
        }
        C(1,kcz[b[i]]);
    }
    for(int i=MX+1;i<=r;++i){
        if(i>l){
            ++cnt;u[cnt]=b[l];v[cnt]=b[i];
        }
        C(1,kcz[b[i]]);
    }
    
    ++cnt;u[cnt]=b[l];v[cnt]=b[MN];
    get(MN,MX);
}

int main(){
    scanf("%d",&n);
    int x;
    for(int i=1;i<=n;++i) {
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;++i){
        scanf("%d",&b[i]);kcz[b[i]]=i;
    }

    for(int i=1;i<=n;++i){
        byc[i]=kcz[a[i]];
    }
    build(1,n,1);
    for(int i=1;i<=n;++i){
        kcz[a[i]]=i;
    }
    get(1,n);
    puts("YES");
    for(int i=1;i<=cnt;++i){
        printf("%d %d
",u[i],v[i]);
    }
    return 0;
}

K Sqrt Approaching

链接

Sqrt Approaching

题意

给出正整数A,B,n,要求C,D满足(A/B<C/D<n^{0.5})(1leq A,B leq 10^{99990}),(2leq n leq 10^{9})

题解

原题面恶心的很。。但式子稍微推一下就变成上面那个题意啦。。
然后我就不会做了QAQ。不管怎么搞总是有很大误差的说。
然后题解神奇的给了个通解但没有给出证明QAQ(而且我也不会证,那这东西就当是公式记住吧)
C=(n+1)A+2nB; D=2A+(n+1)B

(Code)

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL P=998244353;
const int N=2e5+100;
const double PI=acos(-1);

char s[N];
LL a[N],b[N],c[N],d[N];
int L=100000,len;
LL n;
int main(){
    int f;
    for(int i=0;i<=100000;++i) a[i]=b[i]=c[i]=d[i]=0;
    scanf("%s",s+1);len=strlen(s+1);
    int ca=-1;
    for(int i=len;i>=1;--i)a[++ca]=s[i]-'0';
    scanf("%s",s+1);len=strlen(s+1);
    int cb=-1;
    for(int i=len;i>=1;--i)b[++cb]=s[i]-'0';
    scanf("%lld",&n);
    for(int i=0;i<=100000;++i){
        c[i]=(n+1)*a[i]+(LL)2*n*b[i];
        d[i]=(LL)2*a[i]+(n+1)*b[i];
    }
    for(int i=0;i<=100000;++i){
        if(c[i]>=(LL)10){
            c[i+1]+=c[i]/10;
            c[i]=c[i]%(LL)10;
        }
        if(d[i]>=(LL)10){
            d[i+1]+=d[i]/10;
            d[i]=d[i]%(LL)10;
        }
    }
    
    f=0;
    for(int i=100000;i>=0;--i){
        if(c[i]>0) f=1;
        if(f) printf("%d",c[i]);
    }
    puts("");
    f=0;
    for(int i=100000;i>=0;--i){
        if(d[i]>0) f=1;
        if(f) printf("%d",d[i]);
    }
    puts("");
    return 0;
}

H Grouping

链接

Grouping

题意

将2n个数分成n对,每对的价值是两数的差的绝对值,一种分法的价值是每对的价值的方差,问所有分法的期望价值,对998244353取模。(1leq n leq 10^{5})

题解

这题式子其实挺好推的,但是我队开题并不积极,基本都是跟榜。
然后这题基本没几个队过,就没开这题。
现在看来并不难啊。。血亏
过程直接放题解的截图吧。。

(Code)

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL P=998244353;
const int N=2e5+100;
const double PI=acos(-1);
LL ans;
LL n;
LL a[N],s[N],s2[N],g[N],f[N];
LL T[N];
LL qpow(LL x,LL y){
    LL re=1;
    while(y){
        if(y&1) re=re*x%P;
        x=x*x%P;y>>=1;
    }
    return re;
}
int main(){
    LL res;
    scanf("%lld",&n);
    if(n==1){
        puts("0");
        return 0;
    }
    for(int i=1;i<=n+n;++i){
        scanf("%lld",&a[i]);
    }
    sort(a+1,a+1+n+n);
    for(LL i=1;i<=n+n;++i){
        s[i]=(s[i-1]+a[i])%P;
        s2[i]=(s2[i-1]+a[i]*a[i])%P;
        g[i]=(i-1)*a[i]%P*a[i]%P;
        g[i]=(g[i]+s2[i-1])%P;
        g[i]=(g[i]-(LL)2*a[i]*s[i-1])%P;
        g[i]+=g[i-1];
        g[i]=(g[i]%P+P)%P;
        f[i]=(i-1)*a[i]%P-s[i-1];
        f[i]+=f[i-1];
        f[i]=(f[i]%P+P)%P;
    }
    ans=g[n+n];
    res=n*(n+n-1)%P;
    ans=ans*qpow(res,P-2)%P;
    T[0]=T[1]=1;
    for(LL i=2;i<=n;++i) T[i]=T[i-1]*(i+i-1)%P;
    LL val=g[n+n]*T[n-1]%P;
    LL u=f[n+n]*f[n+n]%P,v;
    for(LL i=1;i<=n+n;++i){
        v=0;
        v+=(i-1)*a[i]%P-s[i-1];
        v+=(s[n+n]-s[i])-(n+n-i)*a[i]%P;
        v=(v%P+P)%P;
        v=v*v%P;
        u-=v;
    }
    u=((u+g[n+n])%P+P)%P;
    u=u*T[n-2]%P;
    val=(val+u)%P*qpow(T[n]*n%P*n%P,P-2)%P;
    ans=(ans-val+P)%P;
    cout<<ans<<endl;
    return 0;
}
/*
2
2 2 3 4
748683265
*/
原文地址:https://www.cnblogs.com/Yuigahama/p/13894150.html