Loading

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\)。这样我们胜利的条件是:

\[\dfrac{h}{D}>\dfrac{H}{d} \]

注意是实数比较,这样我们可以变成:

\[hd>HD \]

这表示我们所需要的就是攻击力和防御力乘积相比较。然后考虑最小价值怎么做。

考虑价格的范围比较小,维护一个 \(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\) 赋给哪几条边以及它们的贡献。转移就是:

\[dp[i][j]=\sum_{l=0}^jdp[i-1][l]\times \dbinom{n-1-l}{j-l}\times (k-i+1)^{\frac{(j-l)(j-l-1)}{2}+l\times (j-l)} \]

解释一下,就是枚举上一次已经赋值的 \((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 问题,直接做就行了。

但是好像非常的难写,打算在作死的时候去写写看。先嘴巴着!

posted @ 2022-03-23 09:56  ZCETHAN  阅读(64)  评论(0编辑  收藏  举报