Educational Codeforces Round 167 (Rated for Div. 2) CF 1989 题解
A. Catch the Coin
令一个硬币与原点的x轴方向距离为d。那我们的最优思路肯定是在开局的d秒内走到与硬币x坐标相同、y坐标为-d的位置。如果此时硬币已经掉到这个位置下方那就没救了,否则就肯定可以接到。
时间复杂度\(O(n)\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define pb push_back
#define mpr make_pair
void fileio()
{
#ifdef LGS
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
}
void termin()
{
#ifdef LGS
std::cout<<"\n\nEXECUTION TERMINATED";
#endif
exit(0);
}
using namespace std;
int main()
{
fileio();
int n,x,y;
cin>>n;
rep(i,n)
{
cin>>x>>y;
int xx=0,yy=0,dir=(x>0 ? 1:(x==0 ? 0:-1)),dist=(x==0 ? 0:abs(x));
xx=dir*dist;
yy=-dist;
if(y-dist>=yy-1) puts("YES");
else puts("NO");
}
termin();
}
B. Substring and Subsequence
为了让构造出的串最短,我们要找出b中的最长子串,满足它是a中的一个子序列。令这个子串为\([l,r]\),则我们构造出的最优串为\(b_{[0,l-1]}+a+b_{[r+1,|b|-1]}\)。这个最长子串也很容易求,令\(f_{i,j}\)表示已经匹配到了a的第i个位置、b的第j个位置,且b的第j个位置在当前匹配的子串中的最长匹配长度。转移有两种,分别是\(f_{i,j}\to f_{i+1,j}\)(不再匹配一位)和\(f_{i,j}\to f_{i+1,j+1}\)(再匹配一位,需满足\(a_{i+1}=b_{j+1}\))。
时间复杂度\(O(n^2)\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define pb push_back
#define mpr make_pair
void fileio()
{
#ifdef LGS
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
}
void termin()
{
#ifdef LGS
std::cout<<"\n\nEXECUTION TERMINATED";
#endif
exit(0);
}
using namespace std;
int dp[110][110];
int main()
{
fileio();
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;
string s,t;
cin>>T;
while(T--)
{
cin>>s>>t;
rep(i,s.size()+5) rep(j,t.size()+5) dp[i][j]=0;
rep(i,s.size()) rep(j,t.size())
{
if(s[i]==t[j]) dp[i][j]=max(dp[i][j],1);
if(dp[i][j]==0||i==s.size()-1) continue;
dp[i+1][j]=max(dp[i+1][j],dp[i][j]);
if(j==t.size()-1) continue;
if(s[i+1]==t[j+1]) dp[i+1][j+1]=max(dp[i+1][j+1],dp[i][j]+1);
}
int ans=s.size()+t.size();
rep(j,t.size()) if(dp[s.size()-1][j]>0) ans=min(ans,(int)(s.size()+t.size()-dp[s.size()-1][j]));
cout<<ans<<endl;
}
termin();
}
C. Two Movies
首先如果一个人对两部电影的评价不同,比如一个是1一个是0,那毫无疑问地肯定是选大的那个去评价比较好,我们先把这类人的评价全处理完加到当前评分里,比如令当前两部电影的评分分别为\(t1,t2\)。
然后处理对两部电影评分相同的人。两部都打零分的人没啥用,直接不考虑。令两部都打1/-1分的人数分别为\(pos,neg\)。直接把这些人的评分贪心地均等分给两部电影(因为要让较小值尽量大),比如分配正评分时就先给较小的评分一直加,加到两部电影评分相同为止;相同了之后就你加一次我加一次,尽量保持均等。加负评分时同理。处理时可以先加完所有正评分再加所有负评分,当然反过来也是可以的。
时间复杂度\(O(n)\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define pb push_back
#define mpr make_pair
void fileio()
{
#ifdef LGS
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
}
void termin()
{
#ifdef LGS
std::cout<<"\n\nEXECUTION TERMINATED";
#endif
exit(0);
}
using namespace std;
int t,n,aa[200010],bb[200010];
void ch(int &a,int &b){if(a>b) swap(a,b);}
int main()
{
fileio();
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--)
{
cin>>n;
rep(i,n) cin>>aa[i];
rep(i,n) cin>>bb[i];
int a,b,pos=0,neg=0,t1=0,t2=0;
rep(i,n)
{
a=aa[i];b=bb[i];
if(a!=b)
{
if(a>b) t1+=a;
else t2+=b;
}
else
{
if(a>0) ++pos;
else if(a<0) ++neg;
}
}
ch(t1,t2);
int tmp=t1;
t1+=min(t2-t1,pos);pos-=min(t2-tmp,pos);
t1+=pos/2;t2+=pos-pos/2;
ch(t1,t2);
tmp=t2;
t2-=min(t2-t1,neg);neg-=min(tmp-t1,neg);
t1-=neg/2;t2-=neg-neg/2;
cout<<min(t1,t2)<<endl;
}
termin();
}
D. Smithing Skill
我们肯定是要对每种金属求出最多能获得的经验数,然后全加起来;并且一种装备锻造了之后一定会熔掉,因为熔掉不仅加材料还加经验。注意到\(a_i-b_i\)最小的装备是性价比最高的,因为消耗得最少。在所有性价比最高的装备里面我们可以随便拿一种出来针对同一种金属不停地锻造再熔掉,直到因为剩余金属量\(<a_i\)而不能锻造为止。再看一眼数据范围发现\(a_i\leq10^6\),而我们知道,当某种金属剩余量为x时,我们的最优方案是在所有\(a_i\leq x\)的装备中选性价比最高的锻造。因此我们可以对所有\(\leq 10^6\)的金属剩余量用一个简单的dp预处理出这个剩余量所能挣到的经验。处理一个很大的剩余量\(c_i(i\in[1,m])\)时,直接先用性价比最高的装备把剩余量砍到\(10^6\)以下,然后调用预处理的值即可。
时间复杂度\(O(n+m+max_a)\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <LL,LL>
#define fi first
#define se second
#define pb push_back
#define mpr make_pair
void fileio()
{
#ifdef LGS
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
}
void termin()
{
#ifdef LGS
std::cout<<"\n\nEXECUTION TERMINATED";
#endif
exit(0);
}
using namespace std;
LL n,m,a[1000010],b[1000010],c[1000010],add[1000010],dp[1000010];
void chmin(LL &a,LL b){if(a>b) a=b;}
int main()
{
fileio();
rep(i,1000005) add[i]=1e18;
cin>>n>>m;
rep(i,n) scanf("%lld",&a[i]);
rep(i,n)
{
scanf("%lld",&b[i]);
b[i]=a[i]-b[i];
chmin(add[a[i]],b[i]);
}
rep(i,m) scanf("%lld",&c[i]);
LL mn=1e18;
for(LL i=1;i<=1000003;++i)
{
chmin(mn,add[i]);
if(mn<=i) dp[i]=dp[i-mn]+1;
}
pii opt=mpr(1e18,1e18);
rep(i,n) opt=min(opt,mpr(b[i],a[i]));
LL ans=0;
rep(i,m)
{
if(c[i]>=opt.se) ans+=(c[i]-opt.se+opt.fi-1)/opt.fi,c[i]-=(c[i]-opt.se+opt.fi-1)/opt.fi*opt.fi;
ans+=dp[c[i]];
}
cout<<ans*2<<endl;
termin();
}
E. Distance to Different
首先注意到只要我们确定了a数组的"分段"(也就是把a分成若干段,每一段中填相同的数且相邻两段的数不同),那b就唯一确定了。那么题目中k的限制有什么用呢?唯一的用处就是要你保证分段数\(\geq k\)。
但是,一个可能出现的b数组并不对应着唯一的a。发现问题出在a中长度为2的段。如果a中有一个长度为2的段,且它不在a数组的首尾,那我们把他砍成两个长度为1的段,发现b数组不变。因此我们给合法的a数组定下一个限制:长度为2的段只能出现在a的首尾。思考一下,这样不仅合法的a数组和b数组一一对应了,而且可以让a中的段数尽量多,尽可能满足k的限制。
令\(f_{i,j}\)表示已经把a的前i个位置分段,且分段数与k取min为j的方案数。转移时用前缀和优化到\(O(1)\)。
时间复杂度\(O(nk)\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define pb push_back
#define mpr make_pair
void fileio()
{
#ifdef LGS
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
}
void termin()
{
#ifdef LGS
std::cout<<"\n\nEXECUTION TERMINATED";
#endif
exit(0);
}
using namespace std;
const LL MOD=998244353;
LL n,k,dp[200010][20],sum[200010][20];
int main()
{
fileio();
cin>>n>>k;
dp[0][0]=sum[0][0]=1;
repn(i,n)
{
if(i==2) dp[i][1]=1;
if(i==n)
{
repn(j,k) dp[i][j]=dp[i-2][j-1];
(dp[i][k]+=dp[i-2][k])%=MOD;
}
repn(j,k)
{
(dp[i][j]+=dp[i-1][j-1])%=MOD;
if(i-3>=0) (dp[i][j]+=sum[i-3][j-1])%=MOD;
}
(dp[i][k]+=dp[i-1][k])%=MOD;
if(i-3>=0) (dp[i][k]+=sum[i-3][k])%=MOD;
rep(j,k+1) (sum[i][j]=sum[i-1][j]+dp[i][j])%=MOD;
}
cout<<dp[n][k]<<endl;
termin();
}
F. Simultaneous Coloring
研究中,待更