Educational Codeforces Round 170 (Rated for Div. 2)
A. Two Screens
题意
给你两个字符串 \(s,t\) ,和两个空的字符串,现在你有两种操作:
- 在一个字符串末尾添加一个字母。
- 将一个字符串替换为另一个字符串。
问你让两个空的字符串和 \(s,t\) 相同最少需要多少次操作。
思路
贪心。替换操作至多只会进行一次,且只能替换前缀相同的部分。维护一下最长公共前缀即可。
代码
void solve()
{
string s,t;
cin>>s>>t;
int ans=s.size()+t.size(),num=0;
for(int i=0;i<min(s.size(),t.size());i++)
{
if(s[i]!=t[i]) break;
else num++;
}
if(num)cout<<ans-num+1<<endl;
else cout<<ans<<endl;
}
B. Binomial Coefficients, Kind Of
题意
给你 \(t\) 个 \(n,k\) ,对于每一个 \(n,k\) 都让你求一下这个递推式 \(C_{n,k}=C_{n-1,k}+C_{n-1,k-1}\) 。
思路
场上看样例猜的结论。答案其实就是 \(2^k\) 。
至于为什么我也来不及证明了,签到题秒了就行。
代码
int qpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
void solve()
{
int n;
cin>>n;
vector<int> a(n+1),b(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++) cout<<qpow(2,b[i])%mod<<endl;
}
C. New Game
题意
给你 \(n\) 个数 \(a_i\) ,你第一次可以任意的选择一个数,之后你选的数只能和上一次相等或者是上一次选的数 \(+1\) 。然后数字的种类不能超过 \(k\) 。问你最多选几个数。
思路
一开始我还想着离散化,结果发现排个序,用个队列就能过,类似于滑动窗口,然后统计一下这个队列里有多少种不同的数,每种数出现了多少次,然后答案对队列的大小取个 \(\text{MAX}\) 。
你说的对,但是这题我场上唐完了,写了一半没写完就交上了,害的我多了两罚罚时qwq。
代码
void solve()
{
int n,k;
cin>>n>>k;
vector<int> a(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
sort(a.begin()+1,a.end());
queue<int> q;
map<int,int> num;
int ans=0,cnt=0;
for(int i=1;i<=n;i++)
{
if(q.empty())
{
q.push(a[i]);
num[a[i]]++;
cnt++;
ans=max(ans,(int)q.size());
}
else
{
if(q.back()==a[i])
{
q.push(a[i]);
num[a[i]]++;
ans=max(ans,(int)q.size());
}
else
{
if(a[i]==q.back()+1)
{
q.push(a[i]);
num[a[i]]++;
cnt++;
while(q.size() && cnt>k)
{
int x=q.front();q.pop();
num[x]--;
if(!num[x]) cnt--;
}
ans=max(ans,(int)q.size());
}
else
{
while(q.size())
{
int x=q.front();q.pop();
num[x]--;
if(num[x]==0) cnt--;
}
q.push(a[i]);
}
}
}
}
cout<<ans<<endl;
}
D. Attribute Checks
题意
你现在有“知识”和“力量” 两项能力值,游戏过程中会有 \(m\) 个属性点,每次加属性可以选择增加“力量”或者“知识”。同时游戏中还会出现”能力考验“,当你对应的能力值大于等于游戏要求的能力值时获得 \(1\) 分。
现在给你一个序列 \(a_i\) :
- 当 \(a_i=0\) 时,你可以选择一项能力值 \(+1\)。
- 当 \(a_i>0\) 时,进行知识考验。
- 当 \(a_i<0\) 时,进行力量考验。
现在问你如何分配能力值才能获得的分数最大。
思路
这题真是一眼dp,设 \(dp_{i,j}\) 表示在第 \(i\) 个 \(a_i=0\) 的位置知识能力值为 \(j\) 时能获得的最大分数。
转移的话就要考虑这个能力值给知识还是力量。然后他的贡献就是第 \(i\) 个 \(a=0\) 的位置和第 \(i-1\) 个 \(a=0\) 的位置之间有多少个能力考验的数值低于我 \(i-1\) 这个位置对应的能力值的。由于数据范围很小,我们可以开 \(m\) 个前缀和优化这个过程。最后时间复杂度 \(O(m^2)\) 。转移方程代码里写了,我这里就不写了。
代码
void solve()
{
int n,m,cnt=0;
cin>>n>>m;
vector<int> a(n+1);
vector<vector<int>> sum1(5002,vector<int>(5002));
vector<vector<int>> sum2(5002,vector<int>(5002));
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]==0) cnt++;
else if(a[i]>0)
sum1[cnt][a[i]]++;
else
sum2[cnt][abs(a[i])]++;
}
if(a[n]!=0) m++;
for(int j=1;j<=m;j++)
{
for(int i=1;i<=m;i++)
{
sum1[i][j]+=sum1[i][j-1];
sum2[i][j]+=sum2[i][j-1];
}
}
// for(int i=0;i<=m;i++)
// {
// for(int j=0;j<=5;j++) cout<<sum1[i][j]<<' ';
// cout<<endl;
// }
vector<vector<int>> dp(m+2,vector<int>(m+2));
int ans=0;
for(int i=1;i<=m;i++)
{
for(int j=0;j<=i;j++)
{
int k=i-j;
dp[i][j]=max(dp[i][j],dp[i-1][j]);
if(j)
{
if(k) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+sum1[i-1][j-1]+sum2[i-1][k]);
else dp[i][j]=max(dp[i][j],dp[i-1][j-1]+sum1[i-1][j-1]);
}
if(k)
{
if(j) dp[i][j]=max(dp[i][j],dp[i-1][j]+sum2[i-1][k-1]+sum1[i-1][j]);
else dp[i][j]=max(dp[i][j],dp[i-1][j]+sum2[i-1][k-1]);
}
ans=max(ans,dp[i][j]);
}
}
cout<<ans<<endl;
}