2023牛客寒假算法基础集训营1个人补题 A C D H K L M E F G B

2023牛客寒假算法基础集训营1个人补题 A C D H K L M E F G B

A

题意

给定一个长度为10的得分结果,问至少在第几轮可以确定比赛结果。

思路

签到,就是贪心的思考在每一次结果出来后在劣势方后续最优,优势方最劣的情况下是否有可能翻盘即可。要注意的点就是每次一定要贪心考虑双方的后续,没注意wa了一发(

代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];

void solve() {
	string s;
	cin>>s;
	s=" "+s;
	int p=0,q=0;
	for(int i=1;i<=10;i++) {
		if(i%2) {
			if(s[i]=='1') {
				p++;
			}
			if((10-i)/2+1+q<p||(10-i)/2+p<q) {
				cout<<i<<"\n";
				return;
			}
		}
		else {
			if(s[i]=='1') {
				q++;
			}
			if((10-i)/2+p<q||(10-i)/2+q<p) {
				cout<<i<<"\n";
				return;
			}
		}
	}
    if(p!=q) cout<<10<<"\n";
	else cout<<"-1\n";
}

signed main() {
	IOS;
	int t = 1;
	cin >> t;
	for (int i = 1; i <= t; i++) {
		solve();
	}
}

C

题意

给一个H系数概念(有至少H篇论文的引用量大于等于H),并给定n篇论文的引用量,问分配最大的时候所有h系数之和是多少。

思路

阅读理解,理解完题目后第一反应是排序的贪心,因为引用为1的论文最好就只给一个人,这样保证不会浪费,然后写着写着突然发现好像就是非0的论文篇数,加上感觉贪心有点小问题(赛时没有办法找到详细证明证明这样分n个人一定够用),猜了一发就直接过了。

代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];

void solve() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];
	sort(a + 1, a + n + 1);
	int num = upper_bound(a + 1, a + n + 1, 0) - a ;
	cout<<n-num+1<<"\n";

}

signed main() {
	IOS;
	int t = 1;
	cin >> t;
	for (int i = 1; i <= t; i++) {
		solve();
	}
}

D

题意

给一个矩形,再给定一个点,要求找到另外一个点使得和之前给定点的交集并集比值最大值。

思路

分类讨论,画图可以发现,当不管在矩形内部还是外部的时候,另外的一个点一定是在矩形的四个点上,当然,也可以再细化讨论,甚至可以直接O(1)做出来。

代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n,m;
int a[N];

void solve() {
	int x,y;
	int xp,yp;
	cin>>x>>y>>xp>>yp;
	int dx[]={0,x,0,x};
	int dy[]={y,0,0,y};
	double res=0;
	if(xp<=x&&yp<=y) {
		for(int i=0;i<4;i++) {
			res=max(res,(1.0*abs(dx[i]-xp)*abs(dy[i]-yp))/(x*y));
		}
	}
	else {
		if(xp<=x) {
			res=max((1.0*xp*y)/(xp*(yp-y)+x*y),(1.0*(x-xp)*y)/((x-xp)*(yp-y)+x*y));
		}
		else if(yp<=y) {
			res=max((1.0*yp*x)/((xp-x)*yp+x*y),(1.0*(y-yp)*x)/((y-yp)*(xp-x)+x*y));
		}
		else {
			res=(1.0*x*y)/(xp*yp);
		}
	}
	cout<<fixed<<setprecision(9)<<res<<"\n";
}

signed main() {
	IOS;
	int t = 1;
	cin >> t;
	for (int i = 1; i <= t; i++) {
		solve();
	}
}

H

题意

给n * n个拼图,现在拿走一块,已知每块拼图基础成本为10,一个凹槽-1,一个凸包+1,求拿走的一块成本是多少。

思路

可以发现很明显就是个思维题,因为不管怎么拼凹槽和凸包数量一定相等,那么只要记录剩下拼图需要凹槽和凸包的个数就够了。(注意读入,第一发写成n,第二发写成n * n,白吃两发罚时)

代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];

void solve() {
	cin>>n;
	int res=0;
	for(int i=1;i<=n*n-1;i++) {
		string s;
		cin>>s;
		for(int j=0;j<4;j++) {
			if(s[j]=='1') res--;
			if(s[j]=='2') res++;
		}
	}
	cout<<10-res<<"\n";
}

signed main() {
	IOS;
	int t = 1;
	cin >> t;
	for (int i = 1; i <= t; i++) {
		solve();
	}
}

K

题意

给定01字符串长度和1的个数,求区间长度为三且1个数少于0个数的01字符串有多少个区间长度为三的个数。

思路

打表,感觉贪心的想就是将最坏的往后堆,前面由100组成循环节,最后把剩下的堆在后面,结果打了几组小数据发现是对的,就直接过了。

代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n, m;
int a[N];

void solve() {
	cin >> n >> m;
	string s;
	s = " ";
	int num1 = m, num0 = n - m;
	for (int i = 1; i <= n; i++) {
		if (i % 3 == 1) {
			if (num1 > 0) {
				num1--;
				s = s + '1';
			}
			else break;
		}
		else {
			if (num0 > 0) {
				num0--;
				s = s + '0';
			}
			else break;
		}
	}
	while(num1!=0) {
		num1--;
		s=s+'1';
	}
	while(num0!=0) {
		num0--;
		s=s+'0';
	}
	int res=0;
	for(int i=3;i<=n;i++) {
		if(s[i]+s[i-1]+s[i-2]-3*'0'>1) res++;
	}
	cout<<res<<"\n";
}

signed main() {
	IOS;
	int t = 1;
	//cin >> t;
	for (int i = 1; i <= t; i++) {
		solve();
	}
}

L

题意

有五个团队,每个团队四个人,每次可以问目标是否在某个团队或者是不是某个人,求问话次数的期望。

思路

数学题,就是直接算概率。

代码

#include<bits/stdc++.h>
using namespace std;
signed main() {
    cout<<32<<"\n";
}

M

题意

m个仙贝,n个人,给一个人x个仙贝增加好感x/剩余数量,每个人只能给一次,求最大好感之和。

思路

被标题误导,一直在试图倒着想找规律,由于最后一次一定增加好感为1,试图根据这个往前推,后来打表的时候尝试dp打表,发现二维dp完全可写,但是时间复杂度 O ( n 3 ) O(n^3) O(n3)卡的有点极限。

代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n,m;
int a[N];

double f[1000][1000];
void solve() {
	cin>>n>>m;
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			for(int k=1;k<=j;k++) {
				f[i][j]=max(f[i][j],f[i-1][j-k]+1.0*k/j);
			}
		}	
	}
	double res=0;
	for(int i=1;i<=m;i++) {
		res=max(res,f[n][i]);
	}
    
	cout<<fixed<<setprecision(9)<<res<<"\n";
}

signed main() {
	IOS;
	int t = 1;
	//cin >> t;
	for (int i = 1; i <= t; i++) {
		solve();
	}
}

E

题意

给定两个三角形,问能否不通过翻转得到。

思路

计算几何,直接套板子就完了,写完wa了好几发,卡了最后将近两个小时,甚至一度怀疑板子错了,结果赛后发现题目中说的L形不一定是直角,而我没看到给定了先后的固定点都是在中间,导致一直wa,最后把判断直角去掉直接过了。。。另外后来又尝试了叉乘之外的一种判断方法,直接判断对应角度,感觉是因为加减比较的时候有可能要加减2pi最后丢失了精度。只能用叉乘判是否同号ac。

代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n, m;
int a[N];

const double eps = 1e-7;
struct Point {
	double x, y;
	Point(double x = 0, double y = 0): x(x), y(y) { } //构造函数
};
typedef Point Vector;
double Cross(Vector A, Vector B) {
	return A.x *  B.y - B.x * A.y;
}//叉乘
//!点 - 点 = 向量(向量BC = C - B)
Vector operator - (Point A, Point B) {
	return Vector(A.x - B.x, A.y - B.y);
}
double Dot(Vector A, Vector B) {
	return A.x * B.x + A.y * B.y;
}//点积(满足交换律)
double Length(Vector A) {
	return sqrt(Dot(A, A));
}//长度
double Polar_angle(Vector A) {
	return atan2(A.y, A.x);
}//与x轴夹角

void solve() {
	Point p[5],q[5];
	for(int i=1;i<=3;i++) {
		cin>>p[i].x>>p[i].y;
	}
	for(int i=1;i<=3;i++) {
		cin>>q[i].x>>q[i].y;
	}

	if(Length(p[3]-p[2])<Length(p[2]-p[1])) {
		swap(p[1],p[3]);
	}
	
	if(Length(q[2]-q[3])<Length(q[2]-q[1])) {
		swap(q[1],q[3]);
	}
	
	if(fabs(Length(q[1]-q[2])-Length(q[2]-q[3]))<eps) {
		cout<<"NO\n";
	}
	else {
		if((Cross(p[1]-p[3],p[3]-p[2])>0&&Cross(q[1]-q[3],q[3]-q[2])>0)||
		   (Cross(p[1]-p[3],p[3]-p[2])<0&&Cross(q[1]-q[3],q[3]-q[2])<0)||
          (fabs(Cross(p[1]-p[2],p[3]-p[2]))<eps&&fabs(Cross(q[1]-q[2],q[3]-q[2]))<eps)) {
			cout<<"NO\n";
		}
		else cout<<"YES\n";
	}
}

signed main() {
	IOS;
	int t = 1;
	cin >> t;
	for (int i = 1; i <= t; i++) {
		solve();
	}
}

F

题意

给一个无向图,可以从任意点出发到任意点,同时可以在途径点下蛋,但是下蛋之后就不能再走到那个点,问有多少种可能的起点终点方案(S,T)

思路

仔细想一下就会发现其实和下蛋多少基本上没关系,只要保证下的蛋在一个连通块内就一定有解,否则无解,所以可以用dfs或者并查集先维护所有连通块,最后直接求解。

代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n, m;
int a[N];
int p[N];
int vis[N];
vector<int>g[N];
void dfs(int u, int pre) {
	if (vis[u]) return;
	vis[u] = pre;
	for (auto v : g[u]) {
		dfs(v, pre);
	}
}
void solve() {
	cin >> n >> m;
	for (int i = 1; i <= m; i++) {
		int u, v;
		cin >> u >> v;
		g[v].push_back(u);
		g[u].push_back(v);
	}
	for (int i = 1; i <= n; i++) cin >> a[i], p[i] = i;
	for (int i = 1; i <= n; i++) {
		dfs(i, i);
	}
	map<int, int>mp;
	for (int i = 1; i <= n; i++) {
		mp[vis[i]]++;
	}
	bool f = 0;
	for (int i = 1; i <= n; i++) {
		if (a[i]) f = 1;
	}
	int res = 0;
	if (!f) {
		for (auto [i, j] : mp) res += j * j;
	} else {
		int cnt = 0;
		int id;
		id = -1;
		for (int i = 1; i <= n; i++) {
			if (a[i]) {
				cnt++;
				if (id == -1 || id == vis[i]) id = vis[i];
				else f = 0;
			}
		}
		if (f) res = mp[vis[id]] * mp[vis[id]];
	}
	cout << res << "\n";
}

signed main() {
	IOS;
	int t = 1;
	//cin >> t;
	for (int i = 1; i <= t; i++) {
		solve();
	}
}

G

题意

你有一个长为n的数组a,你需要支持以下两种操作:

1、输入l,r,k,对区间[l,r]中所有数字执行 a i = f ( a i ) a_i=f(a_i) ai=f(ai)操作k次, f ( x ) = r o u n d ( 10 s q r t ( x ) ) f(x)=round(10sqrt(x)) f(x)=round(10sqrt(x)),round为四舍五入函数。

2、输出当前数组所有数字的和。

思路

第一反应线段树,后来发现k的次数给的太多了有点不对,又细看了下打了个表发现了规律:当数小于等于99,在12次操作之内一定变成99且不发生变化;大于等于100时也是在12次内变成100不变。于是我就尝试直接进行了数据结构的模拟,并且当数等于99,100时就可以不再维护它了,那么其实真正计算的次数并不多,但是比较复杂,大量使用了数据结构。

代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n,m;
int a[N];


int sum;
set<PII>num;
int f(int tmp) {
	int cnt=0;
	while(tmp!=99&&tmp!=100) {
		tmp=10*sqrt(1.0*tmp)+0.5;
		cnt++;
		//cout<<tmp<<"\n";
	}
	return cnt;
}
void solve() {
	cin>>n>>m;

	for(int i=1;i<=n;i++) {
		cin>>a[i];
		sum+=a[i];
	}
	for(int i=1;i<=n;i++) {
		if(a[i]!=99&&a[i]!=100&&a[i]!=0) {
			int k=f(a[i]);
			num.insert({i,k});
		}
	}
	for(int i=1;i<=m;i++) {
		int op;
		cin>>op;
		if(op==1) {
			int l,r,k;
			cin>>l>>r>>k;
			vector<int>v;
			for(auto it=num.lower_bound({l,0});it!=num.end();it=next(it)) {
				if(it->first<=r) {
					if(it->second<=k) {
						v.push_back(it->first);
						if(a[it->first]>=100) sum=sum-a[it->first]+100;
						else sum=sum-a[it->first]+99;
						int cnt=it->second;
						int id=it->first;
						num.erase(it);
						num.insert({id,0});
						
					}
					else {
						int cnt=it->second;
						int id=it->first;
						num.erase(it);
						num.insert({id,cnt-k});
						sum-=a[id];
						int kk=k;
						while(kk--) {
							a[id]=10*sqrt(1.0*a[id])+0.5;
						}
						sum+=a[id];
					}
				}
				else break;
			}
			for(auto i:v) {
				num.erase({i,0});
			}
		}
		else {
			cout<<sum<<"\n";
			//for(int i=1;i<=n;i++) cout<<a[i]<<" \n"[i==n];
		}
	}
}

signed main() {
	IOS;
	int t = 1;
	//cin >> t;
	for (int i = 1; i <= t; i++) {
		solve();
	}
}

B

思路

题意有点复杂,直接说赛后的补题思路,其实看到数据范围就可以想到四维dp了,但是难点在于如何优化时间复杂度,因为朴素版本的时间复杂度是 O ( n m x 2 y 2 ) O(nmx^2y^2) O(nmx2y2),后来看题解发现,我们可以通过使用前缀和的方式,在每次枚举完前n队m比分的时候直接前缀和预处理出来后续转移所需的式子,这样就可以将复杂度降低到 O ( n m x y ) O(nmxy) O(nmxy)

代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 998244353 ;
int f[2][121][121][121] ;
int sum1[121][121][121], sum2[121][121][121], sum3[121][121][121] ; // sum1对角线 sum2上三角 sum3 下三角
void add(int &x, int y) {
	x += y ;
	if (x >= mod) x -= mod ;
}
void del(int &x, int y) {
	x -= y ;
	if (x < 0) x += mod ;
}
void solve() {
	int n, m, x, y ;
	cin >> n >> m >> x >> y ;
	f[0][0][0][0] = 1 ;
	int win = n * 3 ;
	for (int i = 1, op = 1; i <= n; ++i, op ^= 1) {
		memset(f[op], 0, sizeof(f[op])) ;
		memset(sum1, 0, sizeof(sum1)) ;
		memset(sum2, 0, sizeof(sum2)) ;
		memset(sum3, 0, sizeof(sum3)) ;
		for (int s = 0; s <= win; ++s)
			for (int a = 0; a <= x; ++a) for (int b = 0; b <= y; ++b) {
					sum1[s][a][b] = f[op ^ 1][s][a][b] ;
					if (a > 0 && b > 0) add(sum1[s][a][b], sum1[s][a - 1][b - 1]) ;
					if (b > 0) {
						add(sum3[s][a][b], sum3[s][a][b - 1]) ;
						add(sum3[s][a][b], sum1[s][a][b]) ;
					} else sum3[s][a][b] = f[op ^ 1][s][a][b] ;
					//cout<<i<<' '<<s<<' '<<a<<' '<<b<<' '<<sum1[s][a][b]<<' '<<sum3[s][a][b]<<'\n' ;
				}
		for (int s = 0; s <= win; ++s)
			for (int b = 0; b <= y; ++b) for (int a = 0; a <= x; ++a) {
					if (a > 0) {
						add(sum2[s][a][b], sum2[s][a - 1][b]) ;
						add(sum2[s][a][b], sum1[s][a][b]) ;
					} else sum2[s][a][b] = f[op ^ 1][s][a][b] ;
					//cout<<i<<' '<<s<<' '<<a<<' '<<b<<' '<<sum2[s][a][b]<<'\n' ;
				}
		for (int s = 0; s <= win; ++s)
			for (int a = 0; a <= x; ++a) for (int b = 0; b <= y; ++b) {
					if (s >= 1) add(f[op][s][a][b], sum1[s - 1][a][b]) ; //平
					if (b > 0) add(f[op][s][a][b], sum3[s][a][b - 1]) ; //输
					if (s >= 3 && a > 0) add(f[op][s][a][b], sum2[s - 3][a - 1][b]) ; //胜
				}
	}
	int ans = 0 ;
	for (int i = m; i <= win; ++i) add(ans, f[n & 1][i][x][y]) ;
	cout << ans << '\n' ;
}
signed main() {
	IOS;
	int t = 1;
//	cin >> t;
	for (int i = 1; i <= t; i++) {
		solve();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值