洛谷秋令营考试题解

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

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

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递减,所以函数是单谷的

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

扫描二维码关注公众号,回复: 7645101 查看本文章
#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

猜你喜欢

转载自www.cnblogs.com/knife-rose/p/11747794.html