2023牛客暑期多校训练营5
之前落下的每一场比赛都是要补回来的。。。
G Go to Play Maimai DX
题解的想法比较简单,由于找到满足1,2,3出现至少一次,4出现至少k次的最短区间,所以可以双指针,双指针用于这种长度最短,长度越长越容易满足条件的题就很恰当。
我没想到双指针,就写的比较麻烦,预处理每个数后一个1,2,3的位置,以及4的特殊处理,每次枚举左端点,计算右端点即可。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define db double
#define endl '\n'
#define rep(x,y,z) for(int x=y;x<=z;++x)
#define fep(x,y,z) for(int x=y;x>=z;--x)
using namespace std;
const int N=1e5+10;
int n,k,a[N],nex[N][5],pl[5],p[N],ct[N],tot;
int main()
{
// freopen("1.in","r",stdin);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>k;
rep(i,1,n) cin>>a[i];
rep(i,1,3) pl[i]=n+1;
fep(i,n,1)
{
rep(j,1,3) nex[i][j]=pl[j];
pl[a[i]]=i;
ct[i]=tot;
if(a[i]==4)
{
p[++tot]=i;
ct[i]=tot;
}
}
int ans=n;
rep(i,1,n)
{
int mx1=max(max(nex[i][1],nex[i][2]),nex[i][3]);
if(mx1==n+1) continue;
if(ct[i]<k) continue;
mx1=max(mx1,p[ct[i]-k+1]);
ans=min(ans,mx1-i+1);
}
cout<<ans<<endl;
return 0;
}
D Cirno's Perfect Equation Class
没啥难度,按照题意模拟,暴力枚举c的所有因子即可。
C Cheeeeen the Cute Cat
有意思的题目,题目让你明求最大匹配,那么说明正解要么和二分图没关系(大概率),要么就是二分图匹配算法上做一些优化。
其实题目已经提示的很明显了,当提到i和i+n无边,i和i+n,j和j+n之间只能存在一条边时,这就提示我们要将i和i+n合并看,而且题目规定边数为n*(n-1)/2,这个数字也很特殊,不就是完全图吗?合并的时候就有一个问题,考虑方向吗?这个时候我们就要观察二分图的匹配在合并时的特点,发现如果合并时按照无向边合并,则无法有效区分是i到j+n的连边还是j到i+n的连边,所以为了区分,按照有向的合并,i到j+n表示i到j有边,这个时候合并起来的图就是一个竞赛图,之后继续考虑匹配在合并后的图上的体现,简单手玩之后可以发现,一系列的匹配在合并后的图上体现为一些路径,也就是说我们要在新图上选择若干条互不相交的路径,这个时候就需要用到一些性质了。竞赛图必有哈密顿路径,所以答案至少为n-1,接下来只需要考虑答案什么时候是n,即存在哈密顿回路。对于有向图的一个点数大于等于3的强连通分量,肯定存在哈密顿环,(图中不存在强连通分量个数为2的)。即只需要判掉个数为1的强连通分量即可。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define db double
#define endl '\n'
#define rep(x,y,z) for(int x=y;x<=z;++x)
#define fep(x,y,z) for(int x=y;x>=z;--x)
using namespace std;
const int N=3010;
int n,du[N];
inline bool check()
{
rep(i,1,n) rep(j,1,n)
{
if(i!=j&&du[i]+du[j]<n) return false;
}
return true;
}
int main()
{
// freopen("1.in","r",stdin);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
rep(i,1,n) rep(j,1,n)
{
int x;cin>>x;
du[i]+=x;
}
sort(du+1,du+n+1);
int j=0,s=0;
rep(i,1,n)
{
s+=du[i];
if(s==i*(i-1)/2)
{
if(i-j<3)
{
cout<<n-1<<endl;
return 0;
}
j=i;
}
}
cout<<n<<endl;
return 0;
}
H Nazrin the Greeeeeedy Mouse
挺有意思的DP。
首先观察数据发现数据特征n,ai,size都很小,但m很大,考虑每一轮可以拿多个奶酪,所以200个奶酪最多有200轮是有用的,所以直接从m中取后n个最大的size即可。
首先想到的是DP,用f[i][j]表示前i个奶酪,用到第j轮的最大吃到的美味值。
发现转移的时候缺少某个个区间用某个size的最大美味值的信息,故这个信息也需要DP。
根据要求,设g[i][j][k]表示从i到j用k的大小,最多迟到的美味值。
转移的话,g[i][j][k]=max(g[i][j-1][k],g[i][j-1][k-a[j]);
总复杂度n^3.
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define db double
#define endl '\n'
#define rep(x,y,z) for(int x=y;x<=z;++x)
#define fep(x,y,z) for(int x=y;x>=z;--x)
using namespace std;
const int N=210,M=1e5+10;
int n,m,a[N],b[N],g[N][N][N],f[N][N],sze[M];
int main()
{
// freopen("1.in","r",stdin);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
rep(i,1,n) cin>>a[i]>>b[i];
rep(i,1,m) cin>>sze[i];
int t=min(m,n);
sort(sze+1,sze+m+1,[&](int a,int b){return a>b;});
reverse(sze+1,sze+t+1);
rep(i,1,n) rep(j,i,n) rep(k,0,sze[t])
{
g[i][j][k]=g[i][j-1][k];
if(k>=a[j]) g[i][j][k]=max(g[i][j][k],g[i][j-1][k-a[j]]+b[j]);
}
// cout<<g[1][1][3]<<' '<<g[2][4][7]<<endl;
//前i个奶酪用到当前第j个口袋.
rep(i,1,n) rep(j,1,t)
{
rep(k,1,i) f[i][j]=max(f[i][j],f[k-1][j-1]+g[k][i][sze[j]]);
}
cout<<f[n][t]<<endl;
return 0;
}