Codeforces Round #635 (Div. 2)部分(A~E)题解
虽然打的是div1,但最后半小时完全处于挂机状态,不会做1C,只有个 \(O(n^3)\) 的想法,水了水论坛,甚至看了一下div2的AB,所以干脆顺便写个div2的题解吧,内容看上去还丰富一些(X)
A Ichihime and Triangle
div2的日常构造题,想造个三角形的话,贪的思路就是让三条边长度尽可能接近,那就尽量把三个数都往中间凑一凑,然后发现\((b,c,c)\)是满足要求的,输出即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(x) ((int)(x).size())
typedef long long ll;
typedef pair<int,int> pii;
typedef double db;
int t,a,b,c,d;
int main(){
cin>>t;
while(t--){
cin>>a>>b>>c>>d;
printf("%d %d %d\n",b,c,c);
}
return 0;
}
B Kana and Dragon Quest game
div2的日常贪心题,注意到操作1可能让HP变的更多,而操作2保证HP会越来越少。所以先贪心的使用操作1,直到操作1再用就使得HP变更多了为止,再进行操作二。检查这一通操作后HP是否还大于0即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(x) ((int)(x).size())
typedef long long ll;
typedef pair<int,int> pii;
typedef double db;
int t,x,n,m;
int main(){
cin>>t;
while(t--){
cin>>x>>n>>m;
rep(i,1,n){
if(x/2+10>x){
break;
}
x=x/2+10;
}
rep(i,1,m){
x-=10;
}
if(x>0) puts("NO");
else puts("YES");
}
return 0;
}
C Linova and Kingdom
div2的日常......好吧这题不太日常,算是比较难一些的div2C,是道很有意思的好题。
第一反应的思路肯定是按照深度来贪心,优先选深的,然后你会发现甚至过不了样例,因为可能某一个点虽然很深,但它的儿子很多,所以你选了他之后,他所有儿子的贡献都要减一,可能该选择不如选另一条分支上一个比他浅的点优。
所以在此基础上稍加改变,也就是由于实际上选一个点对答案的贡献是:1、增加了一个深度那么大的值;2、减少了一个儿子数那么多的值。所以,我们对树上的\(i\)号节点,记录\(dep[i]-son[i]\),这才是选择某个点的真正贡献,贪心的选择该值大的i即可。当然,上面的“2”中其实隐含了另一个贪心性质,也就是最优解中某个节点被选择时其所有的儿子一定已经先被选择了,而这是好证的,因为否则的话,我们就可以选择其一个未被选择的儿子,得到更优的结果。
代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(x) ((int)(x).size())
typedef long long ll;
typedef pair<int,int> pii;
typedef double db;
int n,k,u,v,dep[200010],son[200010];
vector<int> g[200010];
ll a[200010];
int dfs(int now,int fa,int d){
dep[now]=d;
int ret=0;
for(auto to:g[now]){
if(to!=fa) ret+=dfs(to,now,d+1);
}
son[now]=ret;
return ret+1;
}
int main(){
cin>>n>>k;
rep(i,1,n-1){
scanf("%d%d",&u,&v);
g[u].pb(v);
g[v].pb(u);
}
dfs(1,1,0);
rep(i,1,n) a[i]=dep[i]-son[i];
sort(a+1,a+1+n);
ll ans=0;
rep(i,n-k+1,n) ans+=a[i];
cout<<ans<<endl;
return 0;
}
D Xenia and Colorful Gems
根据一些奇怪的高中数学知识,很容易想到,三个数字越接近答案越小,但具体怎么接近呢?发现“0 2 4”的答案比“0 1 4”的答案,前者更小一些,因此“枚举第一个数,用第一个数二分第二个数,用二分到的第二个数再二分第三个数”的算法就是错误的。
那咋搞呢?这里需要脑子绕个弯,换个角度来看,我们只要枚举中间的那个数字,二分到离他最近的两个数计算即可。最后的答案取所有这些结果的最小值。该方法的证明也简单,通过反证法,可以说明该方法一定不会漏掉取到最小值的那个三元组。
代码实现上,应该用函数简化编程,否则的话......君不见,有老哥此题怒写200+行
代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(x) ((int)(x).size())
typedef long long ll;
typedef pair<int,int> pii;
typedef double db;
int t;
int pa,pb,pc;
ll a[100010],b[100010],c[100010];
ll ans;
void solve(ll* a,ll* b,ll *c,int pa,int pb,int pc){
ll x,y,z;
rep(i,1,pa){
x=a[i];
int posy=lower_bound(b+1,b+1+pb,x)-b;
if(posy==pb+1) continue;
y=b[posy];
int posz=upper_bound(c+1,c+1+pc,x)-c-1;
if(posz==0) continue;
z=c[posz];
ans=min(ans,(x-z)*(x-z)+(x-y)*(x-y)+(y-z)*(y-z));
}
}
int main(){
cin>>t;
while(t--){
cin>>pa>>pb>>pc;
rep(i,1,pa) scanf("%lld",a+i);
rep(i,1,pb) scanf("%lld",b+i);
rep(i,1,pc) scanf("%lld",c+i);
sort(a+1,a+1+pa);
sort(b+1,b+1+pb);
sort(c+1,c+1+pc);
ans=9e18;
solve(a,b,c,pa,pb,pc);
solve(a,c,b,pa,pc,pb);
solve(b,a,c,pb,pa,pc);
solve(b,c,a,pb,pc,pa);
solve(c,a,b,pc,pa,pb);
solve(c,b,a,pc,pb,pa);
printf("%lld\n",ans);
}
return 0;
}
E Kaavi and Magic Spell
本题赛时我只会 \(O(n^3)\) 的解法,但比赛结束后一看题解我就会 \(O(n^2)\)的了,为什么呢?因为我发现我赛时想的算法其实tmd就是\(O(n^2)\)的...
首先,这题一会儿可以往前添加,一会可以往后添加,感觉好像会有很多种字符串的样子,但其实,因为你最后得到的字符串要以T作为前缀,所以任何时刻,只有那些最终有可能得到T作为前缀的字符串才是我们关心的。
那么,哪些字符串有可能最终得到\(T\)呢,举个例子,\(T\)是abcd的话,长为2的字符串里,可以得到T的就有ab、bc、cd了......吗?其实可能不止这些,例如如果\(S\)是fabcdf的话,那么诸如ff、df的字符串也可以最终得到\(T\)——只要给这堆f往后稍稍就行。
综上,给出这样的状态:\(dp[i][l][r]\)表示当前取了\(S\)的前\(i\)个字符了,这些字符形成的字符串是与\(T\)中的在\(l\)到\(r\)这一段完全相等的,注意按照这个定义,总有\(r-l+1==i\)成立。
那么,如何解决类似上述ff的问题呢?我们可以把\(T\)串的\([m+1,n]\)这一段脑补为填充了许多通配符,可以与任何一个字母匹配(当然,下面的程序里并不是真的这么实现的)
然后,最终的答案就是\(sum(dp[n][1][x])\)啦,之中\(m \leq x \leq n\)。
当然啦,上述过程中的第一维要优化掉,
代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(x) ((int)(x).size())
typedef long long ll;
typedef pair<int,int> pii;
typedef double db;
char s[3010],t[3010];
int n,m;
ll dp[3010][3010],mod=998244353;
int main(){
cin>>(s+1)>>(t+1);
int n=strlen(s+1),m=strlen(t+1);
rep(i,1,n){
int len=i;
rep(l,1,n+1-len){
int r=l+len-1;
if(s[i]==t[l]||l>m){
dp[l][r]+=(l==r)?1:dp[l+1][r];
dp[l][r]%=mod;
}
if(s[i]==t[r]||r>m){
dp[l][r]+=(l==r)?1:dp[l][r-1];
dp[l][r]%=mod;
}
}
}
ll ans=0;
rep(i,m,n){
ans+=dp[1][i];
ans%=mod;
}
printf("%lld\n",ans);
return 0;
}