“蔚来杯“2022牛客暑期多校训练营4

目录

N.Particle Arts(签到)

K.NIO's Sword(枚举)

D.Jobs (Easy Version)(前缀和)

H.Wall Builder II

A.Task Computing


(翻译来自牛客多校群题解,我写题解是记录一下思路qwq)

N.Particle Arts(签到)

思路:算是个阅读题吧,直接把1尽量往前放即可.

#include<bits/stdc++.h>
#define int __int128
using namespace std;
const int N =5e5+10,mod=998244353;
int a[100005];
int cnt[16];
inline __int128 scan()
{
    __int128 x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
         x=x*10+ch-'0';
         ch=getchar();
    }
    return x*f;
}
void print(__int128 x)
{
    if(x < 0)
    {
        x = -x;
        putchar('-');
    }
    if(x > 9) print(x/10);
    putchar(x%10 + '0');
}
void solve()
{
    int n,x,sum=0;
    n = scan();
	
    for(int i=1;i<=n;i++)
    {
        int tot=0;
       	x = scan(); 
        sum+=x;
        while(x)
        {
            cnt[tot++]+=x%2;
            x/=2;
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<15;j++)
        {
            if(cnt[j])
            {
                a[i]+=(1<<j);
                cnt[j]--;
            }
        }
    }
    int zi=0,mu=n*n*n;
    for(int i=1;i<=n;i++)
    {
        zi+=a[i]*a[i]*n*n+sum*sum-2*n*a[i]*sum;
    }
    int ggcd=__gcd(mu,zi);
	print(zi / ggcd);
	printf("/");
	print(mu/ggcd);
    // cout<<zi/ggcd<<"/"<<mu/ggcd<<endl;
    return ;
}
signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    solve();
    return 0;
}

K.NIO's Sword(枚举)

可以由公式

A_{i-1}*10^{ki}+x_{i}=A_{i} (0<=x_{i}<=10^{ki})

推导出下面式子

i-1*10^{ki}+x_{i}\equiv i (mod n)

那就可以直接针对每个i去枚举ki(从0开始枚举),求最小值xi,当满足条件时直接将ki加到记录到结果里即可.

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =5e5+10,mod=998244353;
int pow10[20];
int n;
bool check(int i,int len)
{
    int res=i*pow10[len]%n;
    res=((i+1)-res+n)%n;
    if(res<pow10[len])
        return true;
    else
        return false;
}
void solve()
{
    int len,ans=0;
    cin>>n;
    pow10[0]=1;
    for(int i=1;i<=18;i++)
        pow10[i]=pow10[i-1]*10;
    for(int i=1;i<=n;i++)
    {
        len=0;
        while(true)
        {
            if(check(i,len))
            {
                ans+=len;
                break;
            }
            len++;
        }
    }
    cout<<ans<<endl;
    return ;
}
signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    solve();
    return 0;
}

D.Jobs (Easy Version)(前缀和)

思路:我们开一个类似于前缀和的数组,can[ i ][ j ][ k ]存的值是在第i个公司当你的前两项属性值达到j和k时你的另一个属性值最少是多少.那么又因为j和k最大为400,我们就可以想前缀和那样直接从1开始遍历求出每个公司当两个属性达到某个值时另外一个值只需要多少就可以的结果.最后check一下即可.

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
long long pw[2000006];
int n,len,a,b,c;
int can[12][410][410];
int solve(int x,int y,int z)
{
    int res=0;
    for(int i=1;i<=n;i++)
        if(z>=can[i][x][y])
            res++;
//当我们的第三项属性高于要求,则这个公司会聘用我们
    return res;
}
signed main()
{
    memset(can,0x3f,sizeof can);
    int q,seed1;
    long long ans=0,lastans=0;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&len);
        while(len--)
        {
            scanf("%d%d%d",&a,&b,&c);
            can[i][a][b]=min(can[i][a][b],c);
        }
        for(int j=1;j<=400;j++)
            for(int k=1;k<=400;k++)
                can[i][j][k]=min({can[i][j][k],can[i][j-1][k],can[i][j][k-1]});
//类似前缀和,求出当在第i个公司我们前两个属性为j和k时另一个属性的最小需求
    }   
    int seed;
    scanf("%d",&seed); 
    std::mt19937 rng(seed);
    std::uniform_int_distribution<>u(1,400);	 
    pw[0]=1;
    for(int i=1;i<=q;i++)
        pw[i]=pw[i-1]*seed%mod;
    for (int i=1;i<=q;i++)
    {
        int IQ=(u(rng)^lastans)%400+1;  // The IQ of the i-th friend
        int EQ=(u(rng)^lastans)%400+1;  // The EQ of the i-th friend
        int AQ=(u(rng)^lastans)%400+1;  // The AQ of the i-th friend
        lastans=solve(IQ,EQ,AQ);  // The answer to the i-th friend
        ans+=lastans*pw[q-i];
        ans%=mod;
    }
    printf("%lld",ans);
	return 0;
}

H.Wall Builder II

思路:对于所有的面积进行枚举判断出所有可以拼成的长宽情况再进行check即可.

此处的check就是把这个矩形分成很多行,我们一行行的贪心的去填充(小矩形宽度为1).填充方法为每次把可以填充进去的最大长度的矩形填充进去,最后判断是否可以填充完.(直接搬的队友代码)

# include <bits/stdc++.h>
# define endl '\n'
# define int long long
# define PII pair<int, int>
const int N = 110;
using namespace std;
using LL = long long;
int cnt[N];
void solve()
{
    int n,s=0,x,y,m=1e18;
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        s+=(n-i+1)*i;
        cnt[i]=n-i+1;
    }
    for (int i=1;i*i<=s;i++)
        if((s%i==0)&&(i+s/i<m))
            x=i,y=s/i;
    cout<<(x+y)*2<<endl;
    // cout << "x = " << x << " y = " << y << endl;
    for(int i=1;i<=x;i++)
    {
        int l=y;
        for(int j=n;j>=1;)
        {
            if(j<=l&&cnt[j]>0)
            {
                cout<<y-l<<" "<<i-1<<" ";
                l-=j;
                cout<<y-l<<" "<<i<<endl;
                cnt[j]--;
            }
            else 
                j--;
        }
    }
    return;
}

signed main() 
{
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t --) solve();
    return 0;
}

A.Task Computing(贪心+dp)

思路: 我们对于这些物品的组合方式进行排序的话,就会想到对于两个临近物品进行大小组合出的效果进行排序,我们比较两个相邻物品(第i个和第i+1个),按照不同顺序摆放得到的贡献表示为:

w[i]+p[i]*w[i+1] 和 w[i+1]+p[i+1]*w[i];那么按照这个顺序进行从小到大的排序之后,组合的情况将会成为最优的,在进行组合的话.会发现这个题已经变成了一个背包问题,直接跑01背包即可.

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =1e5+10,mod=998244353;
const int pp=10000;
double f[25];
struct node
{
    double w,p; 
}goods[N];
void solve()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>goods[i].w;
    for(int i=1;i<=n;i++)
    {
        cin>>goods[i].p;
        goods[i].p/=pp;
    }
    sort(goods+1,goods+1+n,[&](node &x1,node &x2){
        return x1.w+x1.p*x2.w<x2.w+x2.p*x1.w;
    });
    for(int i=1;i<=n;i++)
        for(int j=m;j>=1;j--)
            f[j]=max(f[j],f[j-1]*goods[i].p+goods[i].w);
    cout<<fixed<<setprecision(8)<<f[m]<<endl;
    return ;
}
signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值