【vjudge训练记录】11月个人训练赛1

训练情况

赛后反思

被小数据背刺了,吃了几发RE,不过还是调出来了

A题

我们先考虑将连续的 v 先换成 w,之后就是统计子序列 wow 的个数,我们只需要找每个 o 前面有多少个 w,之后有多少个 w,根据乘法原理可知,这个 o 对答案的贡献就是两个相乘,维护前面和后面的 w 我们可以考虑使用前后缀和。

#include <bits/stdc++.h>
#define int long long

using namespace std;

signed main(){
	string s; cin>>s;
	int n = s.size();
	if(n<=2){
		cout<<0<<endl;
		return 0;
	}
	string t;
	for(int i = 0;i<n;i++){
		if(s[i] == 'o') t+="o";
		if(i==0) continue;
		if(s[i] == 'v' && s[i-1] == 'v') t+="w";
	}
	n = t.size();
	vector<int> pre(n+1);
	vector<int> suf(n+1);
	pre[0] = (t[0] == 'w');
	suf[n-1] = (t[n-1] == 'w');
	for(int i = 1;i<n;i++){
		pre[i] = pre[i-1] + (t[i] == 'w');
	}
	for(int i = n-2;~i;i--){
		suf[i] = suf[i+1] + (t[i] == 'w');
	}
	int ans = 0;
	for(int i = 0;i<n;i++){
		if(t[i] == 'o') ans += (pre[i]*suf[i]);
	}
	cout<<ans<<endl;
	return 0;
}

B题

找数列中是否有 \(m\) 个大于 \(\frac{sum}{4m}\) 个元素,我们先直接求个和,再判断每一位是否符合要求即可,注意除法会有精度和取整问题,所以我们考虑把除法移到左边去乘。

#include <bits/stdc++.h>

using namespace std;

int main(){
	int n,m; cin>>n>>m;
	vector<int> a(n + 1);
	int sum = 0;
	for(int i = 1;i<=n;i++) cin>>a[i],sum+=a[i];
	int cnt = 0;
	for(int i = 1;i<=n;i++){
		if(a[i]*4*m < sum) cnt++;
	}
	if(n-cnt>=m) cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
	return 0;
}

C题

很显然要绝对值最接近 \(0\)\(n\) 要减去 \(k\) 倍的 \(m\),并且这个 \(k\) 要足够大,这就是直接 \(n \mod m\) 即可,再和负数的绝对值情况取大值即可。

#include <bits/stdc++.h>
#define int long long
using namespace std;

signed main(){
	int n,m; cin>>n>>m;
	cout<<min(n%m,abs(n%m-m));
	return 0;
}

D题

简单的 DP,我们设计状态 \(dp_{ij}\) 为走到第 \(i\) 行,\(j\) 列的路线数,它可以从它的上一行和上一列转移过来,求一个和即可,记得跳过全部偶数的情况。

#include <bits/stdc++.h>
#define int long long

using namespace std;

int dp[33][33];

signed main(){
	int n,m; cin>>n>>m;
	dp[1][1] = 1;
	for(int i = 1;i<=n;i++){
		for(int j = 1;j<=m;j++){
			if(i%2==0&&j%2==0) continue;
			if(i==1&&j==1) continue;
			dp[i][j] = dp[i-1][j] + dp[i][j-1];
		}
	}
//	for(int i = 1;i<=n;i++){
//		for(int j = 1;j<=m;j++){
//			cout<<dp[i][j]<<" ";
//		}
//		cout<<endl;
//	}
	cout<<dp[n][m]<<endl;
	return 0;
}

E题

我们判断曲线是否会经过圆,我们只需要判断起点和终点一个在圆内,一个在圆外即可,这样无论曲线怎么画都得经过这个圆,直接统计答案即可。

#include <bits/stdc++.h>

using namespace std;

double dis(int x,int y,int xx,int yy){
	return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}

int main(){
	int n; cin>>n;
	vector<int> x(n + 1),y(n + 1),r(n + 1);
	for(int i = 1;i<=n;i++) cin>>x[i];
	for(int i = 1;i<=n;i++) cin>>y[i];
	for(int i = 1;i<=n;i++) cin>>r[i];
	int xa,ya,xb,yb; cin>>xa>>ya>>xb>>yb;
	int ans = 0;
	for(int i = 1;i<=n;i++){
		double res1 = dis(xa,ya,x[i],y[i]);
		double res2 = dis(xb,yb,x[i],y[i]);
		if((res1<r[i]&&res2>r[i])||(res1>r[i]&&res2<r[i]))ans++;
	}
	cout<<ans<<endl;
	return 0;
}

F题

我们发现这是一道最长公共子序列题,但是这题没有办法使用 \(O(n^2)\) 的 DP,但是我们发现这一题是一个排列,我们可以考虑记录每个排列出现的位置,既然要最长公共子序列,我们只需要每一个排列的下标递增即可,通过下标位置的递增,这就可以转换成最长上升子序列,使用二分算法 \(O(nlogn)\) 做。

#include<bits/stdc++.h>

using namespace std;

int main(){
	int n; cin>>n;
	vector<int> a(n + 1);
	vector<int> b(n + 1);
	vector<int> pos(n + 1);
	for(int i = 1;i<=n;i++) cin>>a[i],pos[a[i]] = i;
	for(int i = 0;i<=n;i++) a[i] = INT_MAX;
	for(int i = 1;i<=n;i++) cin>>b[i];
	for(int i = 1;i<=n;i++){
		int p = lower_bound(a.begin() + 1,a.end(),pos[b[i]]) - a.begin();
		a[p] = pos[b[i]];
	}
	int ans = lower_bound(a.begin() + 1,a.end(),a[0]) - a.begin() - 1;
	cout<<ans<<endl;
	return 0;
}

G题

我们想要 gcd 最大,我们显然发现一对数的 gcd 是会小于等于它的较小的数,所以我们构造的数列的 gcd 必定等于最小的 a,之后就是统计能这样构造的数组一共有多少个,只要每个数在 \(a_i\) 的范围内,并且都是那个 gcd (最小的 \(a_i\))的倍数即可,计算出每一位有多少种放置方法,最后根据乘法原理全部乘起来就是答案了。

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int modd = 1e9+7;
signed main(){
	int n; cin>>n;
	vector<int> a(n + 1);
	for(int i = 1;i<=n;i++) cin>>a[i];
	sort(a.begin() + 1,a.end());
	int cnt = 1;
	for(int i=1;i<=n;i++){
		cnt = cnt % modd * (a[i]/a[1])%modd;
	}
	cout<<a[1]<<" "<<cnt%modd<<endl;
	return 0;
}
posted @ 2024-11-03 20:15  MNNUACM_2024ZY  阅读(31)  评论(0编辑  收藏  举报