Educational Codeforces Round 125 (Rated for Div. 2)
A
直接构造横着走然后竖着走,这样最多只要 \(2\) 步。然后特判一下终点是原点以及是勾股数的情况,前者是 \(0\),后者是 \(1\)。
My Code
bool issqr(int x,int y){
int d=sqrt(x*x+y*y);
return d*d==x*x+y*y;
}
void solve(){
int x,y;cin>>x>>y;
if(x==0&&y==0) cout<<"0\n";
else if(issqr(x,y)) cout<<"1\n";
else cout<<"2\n";
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;for(cin>>T;T--;)
solve();
return 0;
}
B
直接贪心就可以了,能加就加,不能加就减。容易证明是对的。
My Code
void solve(){
int n,B,x,y;
cin>>n>>B>>x>>y;
int ans=0,cur=0;
rep(i,1,n){
if(cur+x<=B) cur+=x;
else cur-=y;
ans+=cur;
}
cout<<ans<<'\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;for(cin>>T;T--;)
solve();
return 0;
}
C
由于是删去最短的前缀,考虑如果开头是左括号,那么必然是删两个。如果开头是右括号,那么会删到下一个右括号为止,然后就做完了。
My Code
const int MAXN=5e5+10;
int nxt[MAXN];
void solve(){
int n;string s;cin>>n>>s;
s=" "+s;rep(i,1,n) nxt[i]=-1;
int lst=-1;
rep(i,1,n){
if(s[i]==')'){
if(~lst) nxt[lst]=i;
lst=i;
}
}
int p=1,ans=0;
while(p<=n){
if(s[p]=='('){
if(p<n) p+=2,ans++;
else break;
}
else{
if(nxt[p]==-1) break;
p=nxt[p]+1;ans++;
}
}
cout<<ans<<' '<<n-p+1<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;for(cin>>T;T--;)
solve();
return 0;
}
D
不要看错题,只能雇佣一种单位。于是我们假设我的总攻击力是 \(d\),总防御力是 \(h\),怪物的攻击力是 \(D\),怪物的防御力是 \(H\)。这样我们胜利的条件是:
注意是实数比较,这样我们可以变成:
这表示我们所需要的就是攻击力和防御力乘积相比较。然后考虑最小价值怎么做。
考虑价格的范围比较小,维护一个 \(mx_i\) 表示花费 \(i\) 所能得到的最大 \(hd\) 的值,这东西首先在读入的时候更新一下,然后后面对每个 \(c\) 枚举它的倍数,然后用它来更新。然后取一个前缀 \(\max\) 表示花费小于等于 \(i\) 所能得到的最大 \(hd\)。这样最后二分一下就完了。
My Code
const int MAXN=2e6+10;
struct Units{
int c,d,h;
void input(){cin>>c>>d>>h;}
bool friend operator<(Units a,Units b){return a.c<b.c;}
}unt[MAXN];
struct Monster{
int D,H;
void input(){cin>>D>>H;}
}mon[MAXN];
int mx[MAXN];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,m,C;cin>>n>>C;
rep(i,1,n) unt[i].input();
rep(i,1,n)
mx[unt[i].c]=max(mx[unt[i].c],unt[i].d*unt[i].h);
rep(i,1,C) for(int j=i;j<=C;j+=i)
mx[j]=max(mx[j],mx[i]*(j/i));
rep(i,1,C) mx[i]=max(mx[i],mx[i-1]);
cin>>m;
rep(i,1,m){
mon[i].input();
int l=1,r=C,ans=-1;
while(l<=r){
int mid=(l+r)>>1;
if(mx[mid]>mon[i].D*mon[i].H)
ans=mid,r=mid-1;
else l=mid+1;
}cout<<ans<<' ';
}
return 0;
}
E
下面用 \((x,y)\) 表示 \(x,y\) 之间的边权。
考虑转化题意,也就是说对于不是 \(1\) 的任意两个节点 \(x,y\),都有 \((1,x)\le (x,y),(1,y)\le (x,y)\)。那也就是说,如果我们对 \((1,x)\) 和 \((1,y)\) 赋值了,那 \((x,y)\) 的范围是确定的。这样我们就有一个 \(dp[i][j]\) 表示已经确定了 \(j\) 条边与 \(1\) 相连并且其中边权最大是 \(i\) 的条件下,这 \(j+1\) 个点赋权值的方案数。这样每次考虑把权值 \(i\) 赋给哪几条边以及它们的贡献。转移就是:
解释一下,就是枚举上一次已经赋值的 \((1,x)\) 的 \(x\) 的数量,然后由于有标号,所以需要组合数算一下这次挑哪几个出来,然后我们考虑除了 \((1,x)\) 之外的边,还需要连这多出来的 \(j-l\) 个点之间连边以及和之前 \(l\) 个点连边,这些边的权值都要大于等于 \(i\)。
Thanks to \(\texttt{F}\color{red}{\texttt{elix}}\) & \(\color{orange}{\texttt{Aging1986}}\)。
My Code
// Problem: E. Star MST
// Contest: Codeforces - Educational Codeforces Round 125 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1657/problem/E
// Memory Limit: 512 MB
// Time Limit: 6000 ms
// Author: ZCETHAN
// Time: 2022-03-23 20:47:27
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
using namespace std;
const int MOD=998244353;
const int MAXN=300;
int ksm(int a,int p){
int ret=1;while(p){
if(p&1) ret=ret*a%MOD;
a=a*a%MOD; p>>=1;
}return ret;
}
int C[MAXN][MAXN],dp[MAXN][MAXN];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,k;cin>>n>>k;
rep(i,0,n){
C[i][0]=1;
rep(j,1,i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
}
dp[0][0]=1;
rep(i,1,k){dp[i][0]=1;
rep(j,1,n-1) rep(l,0,j)
dp[i][j]=(dp[i][j]+dp[i-1][l]*C[n-1-l][j-l]%MOD
*ksm(k-i+1,(j-l)*(j-l-1)/2+l*(j-l))%MOD)%MOD;
}cout<<dp[k][n-1]<<'\n';
return 0;
}
F
大失败,又看错题了。确切地说是脑瘫了。
考虑到可以暴力枚举路径上的每个点,这样我们对每个点记两个字符,一个表示 \(x\to y\) 这个点上的字符,另一个表示 \(y\to x\) 这个点上的字符,然后如果在加字符的时候一个点上有超过 \(2\) 种字符就是无解。然后考虑一个点实际上就是要么取第一种,要么取第二种。并且如果取了 \(x\to y\),那么 \(x\to y\) 的路径上的每个点都必须取 \(x\to y\) 留下的字符。这样就变成了一个 2-SAT 问题,直接做就行了。
但是好像非常的难写,打算在作死的时候去写写看。先嘴巴着!