洛谷秋令營考試題解

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

由於是付費考試就不粘題面了,秋令營題目確實很良心,建議購買

19.10.13

T1

30%:枚舉每一個骰子點數 O(6^(x+y))node

60%:分別枚舉兩個骰子的點數,計算你的點數大於對手的方案數,除以總方案數c++

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%:輸出0ide

40%:無腦 n^2函數

100%:無腦三分測試

至於爲何是單谷函數:假設一開始集合位置pos在1,sum1爲pos左邊人的權值,sum2爲pos右邊人的權值,每次pos向右邊移動一個位置,ans+=(sum1-sum2)優化

然而sum1遞增,sum2遞減,因此函數是單谷的ui

可是你都推出這個來了爲何要三分?排完序掃一遍不完事了?atom

#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\n",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\n",((double)(ret>>1)+0.5));
        else printf("%d\n",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\n",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\n",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\n",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
相關文章
相關標籤/搜索