第一次打牛客直接。。。
y1s1牛客的评测系统真的慢,搞得我不想交
B - 划分
题目链接
首先先对数组a[]
逆序贪心可得val(i,j)=a1+a2+⋯+ai×jval(i,j)=a_1+a_2+\dots+a_{i×j}val(i,j)=a1+a2+⋯+ai×j
尝试证明:分析可知我们最终会选择i×ji×ji×j个数组a[]
的数,贪心肯定每个数选的越大越好,尝试每一组的前jjj大的数都是数组中前i×ji×ji×j大的数的子集,即可将原数组分成iii个部分选出前i×ji×ji×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求最大生成树刚好满足上述需求。我们在求dist(u,v)dist(u,v)dist(u,v)时,只走最大生成树上的边一定能保证dist(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]表示对于前iii个人选择jjj个增加ddd 并且不选/选择第iii个人
状态计算:
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;}
}
要加油哦~