目录
涵盖知识点:思维、贪心、区间dp。
比赛链接:传送门
A - Ichihime and Triangle
题意: 找出数对((x,y,z))满足(ale xle ble yle cle zle d)且能够组成三角形
题解: ((b,c,c))
Accept Code:
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int a,b,c,d;
cin>>a>>b>>c>>d;
cout<<b<<" "<<c<<" "<<c<<"
";
}
return 0;
}
B - Kana and Dragon Quest game
题意: 给定原始数字(h),第一种操作可以使(h=leftlfloor frac{h}{2}
ight
floor + 10);第二种操作可以使(h=h-10)。现在问你能否在(n)次操作一和(m)次操作二之后使得(h<=0)。(可以不用完)
题解: 先通过操作一到最接近20的数,然后减去(10m)判断正负。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int x,n,m;
cin>>x>>n>>m;
while(x>20&&n){
x=x/2+10;
n--;
}
x-=m*10;
puts(x>0?"NO":"YES");
}
return 0;
}
C - Linova and Kingdom
题意: (n)节点的树染黑白两色,其中(k)个是白色。现在要求最大化所有的白点到根节点所经过的黑点总和。
题解: 贪心。考虑到存在父节点仍为白色节点,所以贪心条件为深度-子节点个数。因为每个子节点在计算的过程中都会减去父节点这一个白色节点的数量。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
typedef pair<int,int> pii;
vector<int> edg[maxn];
int dep[maxn],siz[maxn];
vector<int> vec;
void dfs(int u,int pre){
for(auto v:edg[u]){
if(v==pre)continue;
dep[v]=dep[u]+1;
dfs(v,u);
siz[u]+=siz[v];
}
vec.push_back(dep[u]-siz[u]);
siz[u]++;
}
int main(){
int n,k;
cin>>n>>k;
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
edg[u].push_back(v);
edg[v].push_back(u);
}
dfs(1,0);
sort(vec.begin(),vec.end(),greater<int>());
ll ans=0;
for(int i=0;i<k;i++)ans+=vec[i];
cout<<ans<<"
";
return 0;
}
D - Xenia and Colorful Gems
题意: 在三种颜色的数字池中分别取(x,y,z),最小化((x-y)^2+(y-z)^2+(z-x)^2)。
题解: 在确定一个池中的数字之后,二分找到另两个池中最接近的数字。三个池依次扫描一遍就行了。
Accept Code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
inline ll calc(ll x,ll y,ll z){return (x - y) * (x - y) + (y - z) * (y - z) + (z - x) * (z - x);}
int n[3],v[3][maxn];
int main(){
int t;
cin>>t;
while(t--){
cin>>n[0]>>n[1]>>n[2];
for(int i=0;i<3;i++){
for(int j=0;j<n[i];j++){
cin>>v[i][j];
}
sort(v[i],v[i]+n[i]);
}
ll ans=6e18+10;
for(int p1=0;p1<3;p1++){
for(int p2=0;p2<3;p2++){
for(int p3=0;p3<3;p3++){
if(p1==p2||p2==p3||p3==p1)continue;
for(int i=0;i<n[p1];i++){
int pos1=lower_bound(v[p2],v[p2]+n[p2],v[p1][i])-v[p2];
int pos2=upper_bound(v[p3],v[p3]+n[p3],v[p1][i])-v[p3];
if(pos1!=n[p2]&&pos2!=0){
ans=min(ans,calc(v[p1][i],v[p2][pos1],v[p3][pos2-1]));
}
}
}
}
}
cout<<ans<<"
";
}
return 0;
}
E - Kaavi and Magic Spell
题意: 给定串(S,T),长度分别为(n,m),和一个空串(A),每次把S的第一个字母删除然后加到A的前面或者后面。问所有的操作的过程中使得A的前缀为(T)的情况有多少种。
题解: 区间dp。(dp_{i,j})描述当前和(T)在区间([i,j])的匹配个数。这里将(T)长度扩展为(n),但是从(m)之后的顺序是任意的。
转移方程:
注意到插入空串的时候前后算是两种,所以没必要特殊处理,把(dp_{i,i-1})初始化成(1)就可以了。
最终所求就是(dp_{1,m})到(dp_{1,n})的总和。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3010;
const int mod = 998244353;
int n, m, dp[maxn][maxn];
string s, t;
bool check(int i, char c){
return i>m||t[i - 1] == c;
}
int main(){
cin >> s >> t;
n = s.length(),m = t.length();
for(int i = 1; i <= n + 1; i++)
dp[i][i - 1] = 1;
for(int d = 1; d <= n; d++){
char c = s[d - 1];
for(int i = 1, j = d; j <= n; i++, j++){
if(check(i, c))dp[i][j] = (dp[i][j] + dp[i + 1][j]) % mod;
if(check(j, c))dp[i][j] = (dp[i][j] + dp[i][j - 1]) % mod;
}
}
int ans = 0;
for(int i = m; i <= n; i++)ans = (ans + dp[1][i]) % mod;
cout << ans << "
";
return 0;
}