• 牛客多校2022第四场A,H,K,N


    D队友补了不太想写了。之后可能会把C(ntt)和L(计算几何)补了

    A题
    背包,贪心,数学
    题意:给定n个物品,每个物品有一个w值和p值,要你从中选出m个并且对这个m个物品排序使得如下公式的值最大。
    请添加图片描述
    对于这个公式,我们显然能看出m物品的先后顺序是对这个公式的答案有影响的。

    我们考虑从中,前,后,三个方向插入第m个物品看对原m-1个物品的价值影响。
    发现中,后方插入后公式推导困难,而前方插入公式推导简单
    前方插入物品为(w,p)那么新的价值就是w+p原价值
    显然可以按照插入价值拍个序,
    对于a插入的价值大于b插入价值的条件就是:Wa+Pa(Wb+Pb
    pre)>Wb+Pb(Wa+Papre)
    化简后有
    Wa+Pa
    Wb>Wb+Pb*Wa

    排序完后从前完后背包并且贪心的先选插入价值大的即可。
    然而一维01背包是从后往前选择物品的,这样我们就让排序规则为Wa+PaWbWa

    #include 
    #include 
    #include 
    using namespace std;
    struct node
    {
        double p,w;
    };
    const int N =1e5+100;
    int n,m;
    node a[N];
    double dp[N];
    bool cmp(const node &a,const node &b)
    {
        return a.w+a.p*b.w<b.w+b.p*a.w;
    }
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
            cin>>a[i].w;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i].p;
            a[i].p/=10000.0;
        }
        sort(a+1,a+n+1,cmp);
        for(int i=1;i<=n;i++)
            for(int j=min(i,m);j>=1;j--)
                dp[j]=max(dp[j],a[i].w+a[i].p*dp[j-1]);
        cout<<fixed<<setprecision(12)<<dp[m]<<endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    H
    构造,数学
    题意:给定一个整数n,则有长度为n砖1块,n-1的2块,一直到长度为1的砖n块,这些砖头宽度均为1
    要求你用上所有砖垒一堵矩形的墙,输出这堵墙周长最小的方案数和每一块砖的左上角以及右下角。

    思路:首先是周长,我们易知面积是一定的,又知area=n*m。所以可以枚举最相近的n和m值

    然后让长度最小的情况下,一层一层的垒砖就可以了

    #include 
    
    using namespace std;
    int l,h,num[200+100];
    int main()
    {
        int t;
        for(cin>>t;t;t--)
        {
            int n,area=0;
            cin>>n;
            for(int i=1;i<=n;i++)
            {
                area+=(n-i+1)*i;
                num[i]=n-i+1;
            }
            for(int i=1;i*i<=area;i++)
            {
                if(area%i==0)
                {
                    l=area/i;
                    h=i;
                }
            }
            cout<<(l+h)*2<<endl;
            for(int i=0;i<h;i++)
            {
                int temp=0;
                while(temp<l)
                {
                    int j=min(n,l-temp);
                    while(!num[j])
                    {
                        j--;
                    }
                    cout<<temp<<" "<<i<<" "<<temp+j<<" "<<i+1<<endl;
                    temp+=j;
                    num[j]--;
                }
            }
        }
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    K
    SB
    题意:变量a要顺序的遍历1到n,当且仅当a取模n同余于i(i代表1-n的第i个数)时算i被遍历了,并且可以继续遍历下一项。a的初始值为0,遍历前可以进行0-x次操作。求遍历整个数组需要的最小操作次数
    操作定义:a=a*10+x(x是从0到9的任意一个数)

    思路:首相易知n为1情况下答案是0.
    遍历第i项时,a的数值取模于n肯定是i-1,也就是有效部分就是i-1,也就是a就可以当做i-1考虑。

    a进行一次操作,能和a取模于n同余的数就多了。
    第一次操作
    [a10%n,a10%n+10)(这个最大值是取不到的)
    第二次操作
    [a1010%n,a1010%n+10*10)
    以此类推

    所以只要让i%n或者i%n+n在这个范围里就好了

    #include
    using namespace std;
    int n,m,ans;
    int calc(int x,int y)
    {
        int cnt=1,len=10;
        x=10*x%n;
        while(1)
        {
            int l=x,r=x+len;
            if((y>=l&&y<r)||(y+n>=l&&y+n<r))
                return cnt;
            ++cnt;
            len*=10;
            x*=10;
            x%=n;
        }
    }
    int main()
    {
        cin>>n;
        if(n==1)
        {
            cout<<0<<endl;
            return 0;
        }
        for(int i=1;i<=n;++i)
        {
            ans+=calc(i-1,i%n);
        }
        cout<<ans<<endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    N
    思维题
    题意:给定n个数,这n个数可以选择任意两个数然后做and和or运算产生两个值,无限次操作之后数组趋于稳定,问这个稳定数组的方差。

    很容易想到二进制状态的位运算,对齐两个数,只有这两个数的二进制位不同时才会交换对应二进制位的0和1.相同时没有变化。所以最终的稳定数组应当是在二进制视角下的两极分化。

    比如:010 110 001最终会变成111 010 000.
    然后就是求方差的过程。
    发现根据数据范围最大到1e25.爆longlong当时int128可以接受。
    另一种方法是优化公式。

    #include 
    
    using namespace std;
    #define ll __int128
    const ll N = 1e5+100;
    ll a[N];
    ll bit[20];
    ll b[N];
    __int128 read()
    {
        __int128 f=1,w=0;
        char ch=getchar();
        while(ch<'0'||ch>'9')
        {
            if(ch=='-')
            f=-1;
            ch=getchar();
        }
        while(ch<='9'&&ch>='0')
        {
            w=w*10+ch-'0';
            ch=getchar();
        }
        return f*w;
    }
    void print(__int128 x)
    {
        if(x<0)
        {
            putchar('-');
            x=-x;
        }
        if(x>9)print(x/10);
        putchar(x%10+'0');
    }
    ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
    }
    int main()
    {
        ll n;
        n=read();
        for(ll i=1;i<=n;i++)
        {
            a[i]=read();
            for(ll j=0;j<=15;j++)
            {
                ll temp=(a[i]>>j);
                if(temp&1)
                {
                    bit[j]++;
                }
            }
        }
        for(ll i=1;i<=n;i++)
        {
            for(ll j=15;j>=0;j--)
            {
                if(bit[j])
                {
                    bit[j]--;
                    b[i]+=1<<j;
                }
            }
        }
        ll sum=0;
        ll sum1=0;
        for(ll i=1;i<=n;i++)
        {
            sum+=b[i];
        }
        for(ll i=1;i<=n;i++)
        {
            sum1+=(n*b[i]-sum)*(n*b[i]-sum);
        }
        ll mm=n*n*n;
        print(sum1/gcd(sum1,mm));
        printf("/");
        print(mm/gcd(sum1,mm));
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    再做两个SA的题今天就下班了。

  • 相关阅读:
    springboot+vue前后端分离框架
    怎么设定make的默认目标
    医学影像相关开源数据集资源汇总
    吞食天地2西瓜魅影 萌新版自通攻略
    基于Python开发的AI智能联系人管理程序(源码+可执行程序+程序配置说明书+程序使用说明书)
    沟通之痛:如何改变?
    “蔚来杯“2022牛客暑期多校训练营7 JK题解
    鸿蒙HarmonyOS NEXT角落里的知识:ArkTS高性能编程实践
    jmeter学习之路知识点概括
    基于C++实现一个支持简单交互绘图小程序
  • 原文地址:https://blog.csdn.net/qq_35866893/article/details/126088435