Educational Codeforces Round 125
Educational Codeforces Round 125
没打,只是来补题的。
F 题是 2—SAT,不会(你看这人多菜)。
A.Integer Moves
给定一个点的坐标,从零点开始,每次移动的欧几里得距离均为整数,求移动到给定点的最小次数。
题不难,但容易被样例解释引入歧途(就像我)
很明显,分三种情况。
- 如果给出点就在零点,就是0
- 如果给出点到零点的欧几里得距离是整数,就是1
- 否则为2
对于每次移动,如果只改变 x 或 y 的坐标,移动的欧几里得距离必然是整数。
所以对于每一个点,最多移动两次即可得到。
#include <bits/stdc++.h>
#define dd double
#define int long long
using namespace std;int T,x,y;bool check(dd x,dd y){if(floor(sqrt(x*x+y*y))==sqrt(x*x+y*y))return 1;return 0;}
signed main(){
cin>>T;while(T--){
cin>>x>>y;
if(!x&&!y)puts("0");
else if(check(x,y))puts("1");
else puts("2");
}return 0;
}
B.XY Sequence
给定数字 \(n,x,y,k\),要求从 0 开始,构造一个长度为 \(n\) 的序列,满足其中的所有数都小于 \(k\),对于每个数,可以是上一个数加 \(x\),也可以是上一个数减去 \(y\),输出最终序列的最大和。
贪心策略不难想到,如果当前数加上 \(x\) 后仍小于 \(k\) 就加,否则减。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int x,y,n,T,b,ans,g;
signed main(){
cin>>T;while(T--){
cin>>n>>b>>x>>y;ans=0;g=0;
for(int i=1;i<=n;i++)
if(g+x<=b)g+=x,ans+=g;
else g-=y,ans+=g;
cout<<ans<<endl;
}return 0;
}
C.Bracket Sequence Deletion
给定一个括号序列,定义 长度大于等于 2 且回文 或 合法的括号序列可以被删去,每次删去最短的括号序列前缀,输出操作次数和最后剩下的括号数。
还是贪心。
- 如果当前括号是左括号,则下一个括号无论是什么都满足题目给出的条件,因为是右括号就匹配,是左括号就回文。
- 如果当点括号是右括号,则下一个出现右括号的位置就是最短前缀的右端点,因为只要出现第二个右括号则当前前缀回文,符合条件,否则只会在后面加左括号,满足加上右括号就回文的条件。
#include <bits/stdc++.h>
#define int long long
using namespace std;const int N=1e6;int n,T,ans1,ans2,f;bool a[N];string s;
signed main(){
cin>>T;while(T--){
ans1=ans2=0;cin>>n;cin>>s;for(int i=0;i<n;i++)a[i+1]=(s[i]=='('?0:1);f=0;
for(int i=1;i<=n;i++){
if(f){if(a[i]){f=0;ans1++;ans2=i;}}
else{
if(!a[i]&&i!=n){i++;ans1++;ans2=i;}
else f=1;
}
}cout<<ans1<<" "<<(n-ans2)<<endl;
}return 0;
}
D.For Gamers. By Gamers
有 \(C\) 钱币,用于购买 \(n\) 种装置打击 \(m\) 种怪物。
对于装置 \(i\),有 价格 \(c_i\),伤害 \(d_i\),血量 \(h_i\) 三个参数。
对于怪物 \(j\),有 伤害 \(D_j\),血量 \(H_j\) 两个参数。
求杀死每种怪物且没有装置死亡的最低花费,若无法做到输出 -1。
每次只能用一种装置,但可以多个。
假如说用的是装置 \(i\),数量为 \(x\),要杀死的怪是 \(j\),如果满足题目要求,则 \(\dfrac{H_j}{d_i \cdot x} < \dfrac{h_i}{D_j}\),将其写成 \(H_j \cdot D_j < h_i \cdot d_i \cdot x\),避免精度问题。
可以想到预处理出对于每个价位,可以产生的最大 \(h_i \cdot d_i \cdot x\) 的值,这样做的时间复杂度是 \(O(\dfrac{C}{1}+\dfrac{C}{2}+\dfrac{C}{3}+\dots+\dfrac{C}{C})=O(C\log_2C)\)
然后二分查找即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int R(){
int x=0,f=0; char ch=getchar();
while(!isdigit(ch)) f|=(ch==45),ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f?-x:x;
}const int N=2e6;
int n,m,c,mx[N],f[N],sum,Q,l,r,mid,g;
signed main(){
n=R();c=R();for(int i=1,C,d,h;i<=n;i++){C=R();d=R();h=R();mx[C]=max(mx[C],d*h);}
for(int i=1;i<=c;i++){
sum=1;
for(int j=i;j<=c;j+=i){
f[j]=max(f[j],mx[i]*sum-1);
sum++;
}
}for(int i=1;i<=c;i++)f[i]=max(f[i],f[i-1]);
Q=R();for(int i=1,d,h;i<=Q;i++){
d=R(),h=R();l=1,r=c;g=d*h;
while(l<=r){
mid=(l+r)>>1;
if(g>f[mid])l=mid+1;
else r=mid-1;
}
if(l>c)cout<<-1<<" ";
else cout<<l<<" ";
}
return 0;
}
E. Star MST
假设有一个 \(n\) 个点的无向连通图,每条边的边权均在 \(\left[1,k\right]\) 之间。
满足其 最小生成树的边权和 和 与节点 1 相连的边的权值和 相等,求方案数。
将题意转化一下:
对于每条边 \(x -> y(x \ne 1,y \ne 1)\),满足其边权大于 \(1->x,1->y\) 的边权。
很明显用 DP。
假设 \(f_{i,j}\) 表示给顶点 1 的 \(j\) 条边分配了权值,且其中最大权值为 \(i\) 时的方案数。
状态转移方程:$$f_{i,k} = \sum \limits_{j<k} f_{i-1,j} \cdot C_{n-j-1,k-j} \cdot pow(K+1-i,j \cdot (k-j) + \dfrac{(k-j) \cdot (k-j-1)}{2})$$
其中 \(C_{n-j-1,k-j}\) 是因为在 \(n-j-1\) 条边中选出 \(k-j\) 条边,将其权值修改为 \(i\)。
因为对于 \(f_{i-1,j}\),选择了 \(j\) 个点向点 1 连边,称之为点集 \(A\),在转移时选择了另外 \(k-j\) 条边,而 点集 \(A\) 连向点集 \(B\) 的边 和 点集 \(B\) 内部每两点之间的边 的边权都应 \(\ge i\),分别对应 \(j\cdot(k-j)\) 和 \(\dfrac{(k-j)\cdot(k-j-1)}{2}\),而因为边权应 \(\ge i\),因此有 \(K+1-i\) 种取值。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int R(){
int x=0,f=0; char ch=getchar();
while(!isdigit(ch)) f|=(ch==45),ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f?-x:x;
}const int N=601,mod=998244353;
int ksm(int x,int n){int s=1;while(n){if(n&1)s=(s*x)%mod;x=(x*x)%mod;n>>=1;}return s;}
int n,k,c[N][N],f[N][N];
signed main(){
n=R()-1;k=R();f[k+1][0]=1;
for(int i=0;i<=300;i++){
c[i][i]=c[i][0]=1;
for(int j=1;j<i;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}for(int i=k;i;i--)
for(int j=0;j<=n;j++)
for(int k=0;k+j<=n;k++)
f[i][j+k]=(f[i][j+k]+f[i+1][j]*ksm(i,j*k+k*(k-1)/2)%mod*c[n-j][k])%mod;
cout<<f[1][n]<<endl;return 0;
}
别管 F 题了。