牛客练习赛 69

第一次打牛客直接。。。
y1s1牛客的评测系统真的慢,搞得我不想交

B - 划分

题目链接
首先先对数组a[]逆序贪心可得 v a l ( i , j ) = a 1 + a 2 + ⋯ + a i × j val(i,j)=a_1+a_2+\dots+a_{i×j} val(i,j)=a1+a2++ai×j
尝试证明:分析可知我们最终会选择 i × j i×j i×j个数组a[]的数,贪心肯定每个数选的越大越好,尝试每一组的前 j j j大的数都是数组中前 i × j i×j i×j大的数的子集,即可将原数组分成 i i i个部分选出前 i × j i×j i×j大。

#include
#include
using namespace std;
const int N=100010;
typedef long long ll;
ll a[N],s[N];
int n;
int x,y;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    cin>>x>>y;
    sort(a+1,a+1+n);
    reverse(a+1,a+1+n);
    for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
    ll res=0;
    for(int i=1;i<=x;i++)
        for(int j=1;j<=y;j++)
               res+=s[i*j];
    cout<<res<<endl;
    return 0;
}

C - 旅行

题目链接
对于一个连通图,尝试去掉一些边,但是最终保证图连通而且留下的边尽量的大,如果我们用kurskal求最大生成树刚好满足上述需求。我们在求 d i s t ( u , v ) dist(u,v) dist(u,v)时,只走最大生成树上的边一定能保证 d i s t ( u , v ) dist(u,v) dist(u,v)最大。选择排列时,对于每条边最少都要经过一次,答案一定不会超过最大生成树上的边权和。尝试构造一种解使得答案等于最大生成树上的边权和:依次选择最小的边的两个点,然后把这条边删去(意思为不能再次选择该边),这样构造即可构造出最优答案。

#include
#include
using namespace std;
typedef long long ll;
const int N=500010;
struct node
{
    int a,b,w;
    bool operator <(const node& o)const
    {
        return w>o.w;
    }
}e[N];
int n,m;
int p[N];
int find(int x)
{
    return x==p[x]?x:p[x]=find(p[x]);
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++) cin>>e[i].a>>e[i].b>>e[i].w;
    for(int i=1;i<=n;i++) p[i]=i;
    sort(e,e+m);
    ll res=0;
    for(int i=0;i<m;i++)
    {
        int a=e[i].a,b=e[i].b,w=e[i].w;
        int pa=find(a),pb=find(b);
        if(pa!=pb)
        {
            p[pa]=pb;
            res+=w;
        }
    }
    cout<<res<<endl;
    return 0;
}

补完2题,发现牛客的思维难度还是挺高的,如果能够推出结论,还是挺好写代码的,以后要多练练这种思维+算法题目。

D - 火柴排队

刚开始看还以为是个数论题数论渣渣不想看数论,其实是个dp
状态表示: f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] f[i][j][0/1]表示对于前 i i i个人选择 j j j个增加 d d d 并且不选/选择第 i i i个人
状态计算:
f [ i ] [ j ] [ 0 ] = f [ i − 1 ] [ j ] [ 0 ] + ( a [ i − 1 ] + d ≤ a [ i ] ) f [ i − 1 ] [ j ] [ 1 ] f[i][j][0]=f[i-1][j][0]+(a[i-1]+d \leq a[i])f[i-1][j][1] f[i][j][0]=f[i1][j][0]+(a[i1]+da[i])f[i1][j][1]
f [ i ] [ j ] [ 1 ] = f [ i − 1 ] [ j − 1 ] [ 0 ] + f [ i − 1 ] [ j − 1 ] [ 1 ] f[i][j][1]=f[i-1][j-1][0]+f[i-1][j-1][1] f[i][j][1]=f[i1][j1][0]+f[i1][j1][1]
很多dp概率实质都是算方案数,然后借用阶乘和逆元算答案。

#include
#include
#include
using namespace std;
typedef long long ll;
const int N=5010;
const ll mod=998244353;
ll a[N],d;
ll f[2][N][2];// f[i][j][0/1] 表示对于前i个人选择j个增加d 并且不选/选择第i个人
int n;
ll fact[N],infact[N];
ll qmi(ll a,ll b,ll p)
{
    ll res=1;
    while(b)
    {
        if(b&1) res=res*a%p;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
void init(int n)
{
    fact[0]=infact[0]=1;
    for(int i=1;i<=n;i++)
    {
        fact[i]=fact[i-1]*i%mod;
        infact[i]=qmi(fact[i],mod-2,mod);
    }
}

int main()
{
    cin>>n>>d;
    init(n);
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+1+n);
    f[0][0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=i;j++)
        {
            if(j) f[i&1][j][1]=(f[i-1&1][j-1][0]+f[i-1&1][j-1][1])%mod;
            f[i&1][j][0]=(f[i-1&1][j][0]+f[i-1&1][j][1]*(a[i-1]+d<=a[i]))%mod;
        }
    }
    for(int i=1;i<=n;i++)
    {
        ll res=((f[n&1][i][0]+f[n&1][i][1])%mod*fact[n-i]%mod*fact[i]%mod*infact[n]%mod+mod)%mod;
        cout<<res<<endl;
    }
}

要加油哦~

你可能感兴趣的:(牛客网,贪心,最小生成树)