第一次打牛客直接。。。
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<iostream>
#include<algorithm>
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<iostream>
#include<algorithm>
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[i−1][j][0]+(a[i−1]+d≤a[i])f[i−1][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[i−1][j−1][0]+f[i−1][j−1][1]
很多dp概率实质都是算方案数,然后借用阶乘和逆元算答案。
#include<cstring>
#include<iostream>
#include<algorithm>
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;
}
}
要加油哦~