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 题了。

posted @ 2022-03-24 19:09  AIskeleton  阅读(61)  评论(0编辑  收藏  举报