zoukankan      html  css  js  c++  java
  • 洛谷秋令营考试题解

    不想打题索性再摸一篇题解

    因为是付费考试就不粘题面了,秋令营题目确实很良心,建议购买

    19.10.13

    T1

    30%:枚举每个骰子点数 O(6^(x+y))

    60%:分别枚举两个骰子的点数,计算你的点数大于对手的方案数,除以总方案数

    100%:设 f [ i ] [ j ]为第 i 个骰子扔出 j 的概率,f [ i ] [ j ] = sum{ f [ i -1 ] [ j - k ] / 6 } (1<=k<=6);

    然而我不想用概率dp,所以我设了 f [ i ] [ j ] 为到了第 i 个骰子总点数为 j 的方案数会爆long long 但是精度要求低,所以用double 存就水过了……

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    inline int read()
    {
        int x=0,f=1;
        char ch;
        for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
        if(ch=='-') f=0,ch=getchar();
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
        return f?x:-x;
    }
    long double f[2][6010];//f[i][j]统计玩家1第i次抛出后得分为j的方案数
    long double g[2][6010];//……玩家2……
    int x,y,t1,t2;
    long double sum1,sum2;
    signed main()
    {
        x=read(),y=read();
        f[0][0]=g[0][0]=1*0.001;
        for(int i=1;i<=x;++i)
        {
            t1^=1;
            for(int k=0;k<=6*i;++k) f[t1][k]=0;
            for(int j=1;j<=6;++j)
            {
                for(int k=6*i;k>=j;--k)
                {
                    f[t1][k]+=f[t1^1][k-j];
                }
            }
        }
        for(int i=1;i<=y;++i)
        {
            t2^=1;
            for(int k=0;k<=6*i;++k) g[t2][k]=0;
            for(int j=1;j<=6;++j)
            {
                for(int k=6*i;k>=j;--k)
                {
                    g[t2][k]+=g[t2^1][k-j];
                }
            }
        }
        for(int i=1;i<=6*x;++i)
        {
            for(int j=1;j<=6*y;++j)
            {
                sum1+=f[t1][i]*g[t2][j];
                if(i>j) sum2+=f[t1][i]*g[t2][j];
            }
        }
        double sum=sum2/sum1*100.0;
        printf("%.2lf",sum);
        putchar('%');
    return 0;
    }
    View Code

    T2

    10%:输出0

    40%:无脑 n^2

    100%:无脑三分

    至于为什么是单谷函数:假设一开始集合位置pos在1,sum1为pos左边人的权值,sum2为pos右边人的权值,每次pos向右边移动一个位置,ans+=(sum1-sum2)

    然而sum1递增,sum2递减,所以函数是单谷的

    但是你都推出这个来了为什么要三分?排完序扫一遍不完事了?

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    inline int read()
    {
        int x=0,f=1;
        char ch;
        for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
        if(ch=='-') f=0,ch=getchar();
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
        return f?x:-x;
    }
    int n,pos;
    struct node
    {
        int id,w;
        inline bool operator < (const node &t) const
        {
            return id<t.id;
        }
    }a[1000010];
    int sum1,sum2,ret;
    signed main()
    {
        pos=n=read();
        for(int i=1;i<=n;++i) a[i].id=read();
        for(int i=1;i<=n;++i) a[i].w=read();
        sort(a+1,a+n+1);
        for(int i=1;i<n;++i)
            sum1+=a[i].w;
        sum2=a[n].w;
        while(sum1>sum2&&pos>0)//扫一遍
        {
            --pos;
            sum1-=a[pos].w;
            sum2+=a[pos].w;
        }
        for(int i=1;i<=n;++i)
            ret+=(a[i].w*abs(a[i].id-a[pos].id));
        printf("%lld
    ",ret);
    return 0;
    }
    View Code

    T3

    比较高级,看了题目还以为是个啥DP,听大佬在旁边yy平衡树加单调队列优化DP一阵%%%,然而正解是基础算法……

    考虑把一段点分成一组,其中最大纵坐标为maxy,最小为miny,那么线段取在(maxy+miny)/ 2 处代价最小;

    所以可以二分一个代价,从前向后扫点,每次扫到一个点,看看加入这个点会不会令代价超过mid,不超过就加入上一个集合,超过就新开一个集合,可以拿到80pts

    继续优化:每次都要为区间找一个右端点,这个右端点的取值和max和min有关,那么可以用st表预处理出区间最大/最小值,每次确定了左端点后二分区间长度,康康最大值和最小值是否满足要求;

    #include<bits/stdc++.h>
    using namespace std;//开long long 见祖宗
    inline int read()
    {
        int x=0,f=1;
        char ch;
        for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
        if(ch=='-') f=0,ch=getchar();
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
        return f?x:-x;
    }
    int n,m,tyx,tot,maxn,minn=1e9+7;
    struct node
    {
        int x,y;
        inline bool operator < (const node &t) const
        {
            return x<t.x;
        }
    }a[1000010];
    int g[1000010][21][2];
    inline bool check(int d)
    {
        int sum=0,now=1;
        while(now<=n)
        {
            int r=now,maxn=0,minn=1e9+7;
            for(int i=20;i>=0;--i)
            {
                if(r+(1<<i)-1>n) continue;
                int tmin=g[r][i][0],tmax=g[r][i][1];
                if(max(tmax,maxn)-min(tmin,minn)<=d)
                {
                    maxn=max(maxn,tmax);
                    minn=min(minn,tmin);
                    r+=(1<<i);
                }
            }
            now=r;
            if(++sum>m) return 0;
        }
        return 1;
    }
    signed main()
    {
        n=read();
        for(int i=1;i<=n;++i)
        {
            a[i].x=read(),a[i].y=read(),maxn=max(a[i].y,maxn);
        }
        sort(a+1,a+n+1);
        for(int i=1;i<=n;++i) g[i][0][0]=g[i][0][1]=a[i].y;
        for(int i=1;i<=20;++i)
        {
            for(int j=1;j+(1<<i)-1<=n;++j)
            {
                g[j][i][0]=min(g[j+(1<<(i-1))][i-1][0],g[j][i-1][0]);
                g[j][i][1]=max(g[j+(1<<(i-1))][i-1][1],g[j][i-1][1]);
            }
        }
        tyx=read();
        while(tyx--)
        {
            m=read();
            int l=1,r=maxn,ret=0;
            while(l<=r)
            {
                double mid=(l+r)>>1;
                if(check(mid)) ret=mid,r=mid-1;
                else l=mid+1;
            }
            if(ret&1) printf("%.1lf
    ",((double)(ret>>1)+0.5));
            else printf("%d
    ",ret>>1);
        }
    return 0;
    }
    View Code

    19.10.26 (这场对于TG来说题目质量真的不错,除了不卡暴力qwq

    T1

    这题目……

    你以为你不会,打了个O(n^2)

    你以为你会了一点,打了个O(nk)

    你以为你会了,打了个O(n)

    然而都是100pts……

    为什么n^2 100pts?

    当gcd(k,10)==1

    假设两个后缀为BA和A,且BAA(%k)

    那么 BA - A ≡ B×(10^|A|)≡ 0 (%k)

    由于gcd(k,10)==1,所以B ≡ 0(%k)

    所以永远不会出现同余的后缀,n^2实际上是nk的

    为什么nk 100pts?

    因为洛谷跑得快……

    这题实际上可以O(n)

    如果gcd(k,10)==1,我们可以求出10的逆元

    假设有后缀ABCD

    其中 BCD0(%k)

    那么 100*B+10*C+D0(%k)

    那么 1000*A+100*B+10*C+D * INV(10^3)A(%k)

    所以我们只要在A处记录一下,然后继续往后扫,在后面康康是否有当前数字%p×inv(10^len)存在就好啦

    #include<bits/stdc++.h>
    using namespace std;
    namespace knife_rose{
        inline int read()
        {
            int x=0;char ch,f=1;
            for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
            if(ch=='-') f=0,ch=getchar();
            while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
            return f?x:-x;
        }
        const int N=1e6+10;
        char s[N];
        int n,p,inv,ret;
        bool vis[110];
        int r[110],num;
        inline int fast(int x,int k)
        {
            int ret=1;
            while(k)
            {
                if(k&1) ret=ret*x%p;
                x=x*x%p;
                k>>=1;
            }
            return ret;
        }
        signed main()
        {
            scanf("%s",s+1);
            n=strlen(s+1);
            p=read();
            if(p==2||p==5)
            {
                for(int i=1;i<=n;++i)
                    if(!((s[i]-'0')%p)) ++ret;
                printf("%d
    ",ret);
                return 0;
            }
            inv=fast(10,p-2);r[++num]=0,vis[0]=1;
            for(int k=inv,tmp=0,f,i=1;i<=n;++i,tmp=tmp*10%p,k=k*inv%p)
            {
                tmp=(tmp+s[i]-'0')%p,f=(tmp*k)%p;
                if(vis[f])
                {
                    ++ret;
                    for(int j=1;j<=num;++j) vis[r[j]]=0;
                    num=0;
                }
                r[++num]=f,vis[f]=1;
            }
            printf("%d
    ",ret);
        return 0;
        }
    }
    signed main()
    {
        return knife_rose::main();
    }
    View Code

    T2

    线段树板子(题目怪吓人的

    可以区间快速合并直接线段树就好了

    其实分块更好想好写,甚至不用区间合并,分完块直接扫过去

    考场上还是写了线段树(还混了个最优解

    #include<bits/stdc++.h>
    using namespace std;
    namespace knife_rose{
    #define ls(p) (p<<1)
    #define rs(p) (p<<1|1)
    #define mid ((l+r)>>1)
        inline int read()
        {
            int x=0;char ch,f=1;
            for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
            if(ch=='-') f=0,ch=getchar();
            while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
            return f?x:-x;
        }
        const int N=3e5+10;
        int n,m;
        int a[N];
        struct node
        {
            int ans,z,y;
        }seg[N<<2];
        inline void push_up(int p)
        {
            seg[p].ans=seg[ls(p)].ans+seg[rs(p)].ans;
            if(seg[rs(p)].z<=seg[ls(p)].y)
            {
                seg[p].ans+=seg[rs(p)].z;
                seg[p].z=seg[ls(p)].z;
                seg[p].y=seg[ls(p)].y+seg[rs(p)].y-seg[rs(p)].z;
            }
            else
            {
                seg[p].ans+=seg[ls(p)].y;
                seg[p].z=seg[ls(p)].z+seg[rs(p)].z-seg[ls(p)].y;
                seg[p].y=seg[rs(p)].y;
            }
        }
        inline void build(int l,int r,int p)
        {
            if(l==r)
            {
                if(a[l]==1) seg[p].y=1;
                else seg[p].z=1;
                return;
            }
            build(l,mid,ls(p));
            build(mid+1,r,rs(p));
            push_up(p);
        }
        inline void update(int pos,int l,int r,int p)
        {
            if(l==r)
            {
                if(a[l]==1) seg[p].y=1,seg[p].z=0;
                else seg[p].z=1,seg[p].y=0;
                return;
            }
            if(pos<=mid) update(pos,l,mid,ls(p));
            else update(pos,mid+1,r,rs(p));
            push_up(p);
        }
        inline node query(int tl,int tr,int l,int r,int p)
        {
            if(tl<=l&&r<=tr) return seg[p];
            if(tr<=mid) return query(tl,tr,l,mid,ls(p));
            else if(tl>mid) return query(tl,tr,mid+1,r,rs(p));
            else
            {
                node tx=query(tl,tr,l,mid,ls(p)),ty=query(tl,tr,mid+1,r,rs(p));
                node tyx;
                tyx.ans=tx.ans+ty.ans;
                if(ty.z<=tx.y)
                {
                    tyx.ans+=ty.z;
                    tyx.z=tx.z;
                    tyx.y=tx.y+ty.y-ty.z;
                }
                else
                {
                    tyx.ans+=tx.y;
                    tyx.z=tx.z+ty.z-tx.y;
                    tyx.y=ty.y;
                }
                return tyx;
            }
        }
        signed main()
        {
            n=read(),m=read();
            for(int i=1;i<=n;++i) a[i]=read();
            build(1,n,1);
            for(int opt,x,y,i=1;i<=m;++i)
            {
                opt=read(),x=read(),y=read();
                if(opt)
                {
                    printf("%d
    ",query(x,y,1,n,1).ans);
                }
                else
                {
                    a[x]=y;
                    update(x,1,n,1);
                }
            }
        return 0;
        }
    }
    signed main()
    {
        return knife_rose::main();
    }
    /*
    10 3
    1 1 0 0 1 0 0 1 1 0
    1 1 10
    0 3 1
    1 1 10
    
    */
    View Code

    T3

    毒瘤出题人,测试点加权弄的暴力就9分(我看见就弃了),结果不卡O(nm)算法,直接成了sb题

    顺便处刑了lyy神犇,它AC自动机代码里有个函数叫fuck,讲师讲题的时候还念出来了233333333

    0-100分无脑哈希,但复杂度其实不太对

    考虑每个串长度互补相同的情况:假设有m个串,长度为1到m

    等差数列求和,总长度为m^2级别

    说明最多有√sum(ti)个串

    那么如果我们每种长度只进行一次匹配,总复杂度就是n√n的

    我们把每个串按长度排序,把长度相同的哈希值放进一个mp数组里,mp数组下标是哈希值,里面存的是编号

    每次取出s中长度为Len 的子串哈希值,看看mp数组里面有没有对应的串,这样就可以求出每个串在哪里被匹配上了

    然后双指针求最小串(二分也可以

    #include<bits/stdc++.h>
    using namespace std;
    namespace knife_rose{
    #define int long long
        inline int read()
        {
            int x=0;char ch,f=1;
            for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
            if(ch=='-') f=0,ch=getchar();
            while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
            return f?x:-x;
        }
        const int N=1e5+10,p=1e6+3,base=131;
        int n,m;
        char txt[N],s[N];
        struct node
        {
            int len,hash;
            inline bool operator < (const node &t) const
            {
                return len!=t.len?len<t.len:hash<t.hash;
            }
        }a[N];
        int len[N],mp[1000010];
        int has[100010],pw[100010];
        vector<int> e[N];
    //    inline void get_hash(int id)
    //    {
    //        for(int i=1;i<=len[id];++i)
    //            a[id].hash=(a[id].hash*base+s[id])%p;
    //        mp[a[id].hash]=id;
    //    }
        int c[N],d[N],tot,tx,ty;
        signed main()
        {
            scanf("%s",txt+1);
            n=strlen(txt+1);
            tx=1,ty=n;
            for(int i=pw[0]=1;i<=n;++i) has[i]=(has[i-1]*base+txt[i])%p,pw[i]=pw[i-1]*base%p;
            m=read();
            for(int i=1;i<=m;++i)
            {
                scanf("%s",s+1);
                a[i].len=strlen(s+1);
                for(int j=1;j<=a[i].len;++j)
                {
                    a[i].hash=(a[i].hash*base+s[j])%p;
                }
    //            for(int l,r=a[i].len;r<=n;++r)
    //            {
    //                l=r-a[i].len+1;
    //                int tmp=((has[r]-has[l-1]*pw[r-l+1])%p+p)%p;
    //                if(tmp==a[i].hash) e[r].push_back(i);
    //            }
            }
            sort(a+1,a+m+1);
            int sum=0;
            for(int i=1;i<=m;++i)
            {
                mp[a[i].hash]=i;
                if(a[i].hash==a[i-1].hash) ++sum;
                if(a[i].len!=a[i+1].len)
                {
                    for(int l,r=a[i].len;r<=n;++r)
                    {
                        l=r-a[i].len+1;
                        int tmp=((has[r]-has[l-1]*pw[r-l+1])%p+p)%p;
                        if(mp[tmp]) e[r].push_back(mp[tmp]);
                    }
                    int now=i;
                    while(a[now].len==a[i].len&&i) mp[a[now--].hash]=0;
                }
            }
            m-=sum;
            for(int sum,l=0,r=1;r<=n;++r)
            {
                sum=e[r].size();
                for(int k=0;k<sum;++k)
                {
                    int t=e[r][k];
                    if(d[t]) --c[d[t]],--tot;//d是上一次出现位置,c是这个位置有没有串出现过
                    ++c[d[t]=(r-a[t].len+1)],++tot;
                }
                if(tot==m)
                {
                    while(!c[l]) ++l;
                    if(r-l<ty-tx) tx=l,ty=r;
                }
            }
            for(int i=tx;i<=ty;++i) putchar(txt[i]);
        return 0;
        }
    }
    signed main()
    {
        return knife_rose::main();
    }
    /*
    potatomatonionandeverything
    3
    potato
    tomato
    onion
    
    */
    View Code
  • 相关阅读:
    javaScript:制作随机验证码
    XSL简明教程
    javascript判断用户使用的浏览器
    jswindow对象的方法和属性资料
    VBscript操作文件
    终于找到组织了...
    公司网站的物流费用设计
    配置live Writer来发blog
    IIS必备的2个插件
    全国默哀 网站首页都要变成灰色的简单解决办法
  • 原文地址:https://www.cnblogs.com/knife-rose/p/11747794.html
Copyright © 2011-2022 走看看