Educational Codeforces Round 125 (Rated for Div. 2)(ABCDE)
A. Integer Moves
题意:大概是说最初在点(0,0),想要到点(x,y),只能走整数距离(不清楚,根据样例猜),问最少多少步
思路:
目标点就是原点,步数0
目标点与原点的直线距离是整数,步数1
否则步数就是2(先走到同x,再同y)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int x,y;
void solve(){
scanf("%d%d",&x,&y);
if(x==0&&y==0) puts("0");
else{
int num=sqrt(x*x+y*y);
if(num*num==x*x+y*y) puts("1");
else puts("2");
}
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
B. XY Sequence
题意:构造一个长度为n的序列,序列中每个元素的值
<
=
B
<=B
<=B,后一个数
a
[
i
+
1
]
=
a
[
i
]
+
x
a[i+1]=a[i]+x
a[i+1]=a[i]+x或
a
[
i
+
1
]
=
a
[
i
]
−
y
a[i+1]=a[i]-y
a[i+1]=a[i]−y,问序列的最大总和是多少
思路:我们知道初始
a
[
0
]
=
0
a[0]=0
a[0]=0,我们每次去找到符合题意的最大的数就行了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
ll n,B,x,y;
void solve(){
scanf("%lld%lld%lld%lld",&n,&B,&x,&y);
ll sum=0,now=0;
for(ll i=1;i<=n;i++){
ll nu1=now+x;
ll nu2=now-y;
if(nu1<=B&&nu2<=B){
if(nu1>=nu2){
now=nu1;
sum+=nu1;
}
else{
now=nu2;
sum+=nu2;
}
}
else if(nu1<=B){
now=nu1;
sum+=nu1;
}
else {
now=nu2;
sum+=nu2;
}
}
printf("%lld\n",sum);
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
C. Bracket Sequence Deletion
题意:只包含
′
(
′
和
′
)
′
'('和')'
′(′和′)′的长度为n的字符串,每次删除前面最短的合法括号序列或回文序列,问最终会操作多少次和最后剩下多少字符
题意很重要,开始读的十分离谱,什么最小剩余的同时的最少操作次数
思路:
我们发现,当前是
′
(
′
'('
′(′,只要后还存在字符,那么都会一起直接删掉。
(
(
或
(
)
(( 或()
((或()
当前是
′
)
′
')'
′)′,后面是
′
)
′
')'
′)′,则构成
)
)
))
))直接删掉;
而后面如果是
′
(
′
'('
′(′,则一直往后面找,知道找到第一个
′
)
′
')'
′)′或末尾为止。
)
(
(
.
.
(
(
)
)((..(()
)((..(()
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n;
char s[N];
void solve(){
scanf("%d",&n);
scanf("%s",s+1);
int use=0,cnt=0;
for(int i=1;i<=n;){
if(s[i]=='('){
if(i!=n){ // "(("或"()"
cnt++;
use+=2;
}
i=i+2;
}
else{
int pos=i+1;
int num=0;
while(pos<=n&&s[pos]=='('){
pos++;
num++;
}
if(pos<=n&&s[pos]==')'){ //")(((...(()"
cnt++;
use+=num+2;
}
i=pos+1;
}
}
printf("%d %d\n",cnt,n-use);
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
D. For Gamers. By Gamers.
题意:
有n种伙伴可以选择,第i种伙伴有
c
i
,
h
i
,
d
i
ci,hi,di
ci,hi,di三个参数,代表选择一个伙伴的花费、每个伙伴的单位时间的攻击力、每个伙伴的生命值。
一共有m个怪物,第j个怪物有
D
j
,
H
j
Dj,Hj
Dj,Hj两个参数,代表每个怪兽的单位时间的攻击力、每个怪兽的生命值。
每一轮我们有C枚金币,我们当前轮只能选择一种伙伴进行战斗,在不超过C枚金币的情况下,我们可以选择多位该类型伙伴,问在没有伙伴阵亡的情况下击败每个怪物的最小花费。 攻击伤害是持续累加的(奇怪)
思路:
我们经过公式转换,我们发现我们可以找到每个金币可以解决的最大怪物乘积???,然后二分答案就行了。
(具体看推导吧)
考虑:
对于第i种伙伴,有ci,hi,di
对于第j个怪物,有Dj,Hj
发现,怪物肯定会十分聪明,只会盯着一个伙伴进行攻击,这样使得我们不满足没有伙伴阵亡的情况,
从而需要增加伙伴数量来加大火力,间接导致花费变高。
我们知道一位伙伴存活时间为: time=hi/Dj, 则我们击败怪物的时间需要严格小于time
设我们需要该类型伙伴数量num,则有: num*time*di>Hj 即num*hi*di>Dj*Hj
花费为money=num*ci
所以当我们对于花费val元,找到最大的hi*di,就可以解决问题
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+5,M=1e6+6;
ll n,C,m,f[M]; //f[i]表示花费i元的最大h*d
struct node1{ll c,d,h;}a[N];
struct node2{ll D,H;}b[N];
void solve(){
scanf("%lld%lld",&n,&C);
for(ll i=1;i<=n;i++){
scanf("%lld%lld%lld",&a[i].c,&a[i].d,&a[i].h);
f[a[i].c]=max(f[a[i].c],a[i].d*a[i].h);
}
scanf("%lld",&m);
for(ll i=1;i<=m;i++) scanf("%lld%lld",&b[i].D,&b[i].H);
for(ll i=1;i<=C;i++){
for(ll j=1;j<=C/i;j++){
f[i*j]=max(f[i*j],f[i]*j);
}
}
for(ll i=1;i<M;i++) f[i]=max(f[i],f[i-1]);
for(ll i=1;i<=m;i++){
ll idx=upper_bound(f+1,f+M,b[i].H*b[i].D)-f;
if(idx>C) printf("-1 ");
else printf("%lld ",idx);
}
}
int main(){
int T=1;
while(T--) solve();
return 0;
}
E. Star MST
题意:
题意:
有n个节点的完全图,问与节点1相连的所有的权值和等于这n个节点构成的最小生成树的图有多少种。
每条边的权值在[1,k]之间,只要有一条边的权值不同,则是不同的两个图
思路:
初始想成在定节点1相连的边的最大权值和最大权值边的数量去解决,发现会少一些情况。
根据大佬思路:
定义:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为所有边权小于等于i的边,已经和1相连的边为j个
我们发现对于边权为i+1的边,我们考虑有l个点与节点1的边为i+1
当前剩下
n
−
1
−
j
n-1-j
n−1−j个点,选其中l个,
C
[
n
−
1
−
j
]
[
l
]
C[n-1-j][l]
C[n−1−j][l]
对于这k个点内部的两两连边,有
l
∗
(
l
−
1
)
/
2
l*(l-1)/2
l∗(l−1)/2条边,且值需要大于
>
=
i
+
1
>=i+1
>=i+1, 即
(
k
−
i
+
1
)
(
l
−
1
)
/
2
(k-i+1)^{(l-1)/2}
(k−i+1)(l−1)/2
对于其他已经与节点1连边的j个点,有
j
∗
l
j*l
j∗l条边,且值需要大于
>
=
i
+
1
>=i+1
>=i+1, 即
(
k
−
i
+
1
)
j
∗
l
(k-i+1)^{j*l}
(k−i+1)j∗l
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=260;
const ll mod=998244353;
ll n,k,sum;
ll dp[N][N];
ll jiec[N+5],inv[N+5];
ll ksm(ll a,ll b){
ll res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
void init(){
jiec[0]=1;
for(ll i=1;i<=N;i++) jiec[i]=jiec[i-1]*i%mod;
inv[N]=ksm(jiec[N],mod-2);
for(ll i=N-1;i>=0;i--) inv[i]=inv[i+1]%mod*(i+1)%mod;
}
ll C(ll a,ll b){return a<b?0:jiec[a]*inv[a-b]%mod*inv[b]%mod;}
void solve(){
init();
scanf("%lld%lld",&n,&k);
dp[0][0]=1;
for(ll i=1;i<=k;i++){
for(ll j=0;j<n;j++){
for(ll l=0;l+j<n;l++){
dp[i][j+l]=(dp[i][j+l]+dp[i-1][j]*C(n-1-j,l)%mod*ksm(k-i+1,(l-1)*l/2+l*j))%mod;
}
}
}
printf("%lld\n",dp[k][n-1]);
}
int main(){
int T=1;
while(T--) solve();
return 0;
}