2021新生赛破防试水

因为又菜又忙所以缓慢不定时更新

菜死了,找个签到题都很难,哭。

A.

签到,pass

M.

贪心。
求最悲观情况下低情商人数:从左往右遍历,遇到1把cnt变为0,cnt在k以下的计入ans2
最乐观情况:从左往右遍历,cnt > k的1把cnt变为0,cnt <= k的全部计入ans1
比较玄学的贪心,因为没有oj不知道对不对,只自己测了几个测试案例
s开到20010 SF了,开到200010 ac了(奇怪)

#include <bits/stdc++.h>
using namespace std;
char s[20010];
int n, k, ans1 = 0, ans2 = 0, cnt = 0, ok = 0;
int main () {
	cin >> n >> k;
	cin >> s;
	for (int i = 0; i < n; i++) {
		cnt ++;
		if (s[i] == '1' ) {
			if (cnt <= k && ok) ans2 ++;
			ok = 1;
			cnt = 0;
		}
	}
	ok = 0, cnt = 0;
	for (int i = 0; i < n; i++) {
		cnt ++;
		if (s[i] == '1') {
			if (cnt <= k && ok) ans1 ++;
			if (cnt > k || !ok) cnt = 0;
			ok = 1;
		}
	}
	cout << ans1 << ' ' << ans2 << endl;
	return 0;
}

\std.out

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(v) ((int)v.size())
#define fs first
#define sc second
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
int n,k,sum;
char s[200010];
int main(){
	cin>>n>>k;
	scanf("%s",s+1);
	assert(n<=200000&&k<=100&&strlen(s+1)==n);
	int lst=-1000000,heq=0,ans=0;
	
	rep(i,1,n){
		if(s[i]=='1'){
			sum++;
			if(i-lst>k){
				heq++;
				lst=i;
			}
		}
	}
	
	lst=-1000000;
	rep(i,1,n){
		if(s[i]=='1'){
			if(i-lst<=k)	ans++;
			lst=i;
		}
	}
	printf("%d %d\n",sum-heq,ans);
	return 0;
}

J.

直接模拟,摆烂了QAQ
暴力枚举行列式

#include <bits/stdc++.h>
using namespace std;
int a[1010][1010];
int s(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
	return a[x1][y1] * a[x2][y2] - a[x3][y3] * a[x4][y4];
}
void solve (int n, int c) {
	if (n == 1) cout << c << endl;
	else if (n == 2) {
		int ans1 = c * a[2][2] - a[1][2] * a[2][1]; int res = ans1;
		int ans2 = a[1][1] * c - a[1][2] * a[2][1]; res = max(res, ans2);
		int ans3 = a[1][1] * a[2][2] - c * a[2][1]; res = max(res, ans3);
		int ans4 = a[1][1] * a[2][2] - a[1][2] * c; res = max(res, ans4);
		cout << res << endl;
	}
	else {
		int ans1 = c * s(2,2,3,3,2,3,3,2) - a[1][2] * s(2,1,3,3,2,3,3,1) + a[1][3] * s(2,1,3,2,2,2,3,1); int res = ans1;
		int ans2 = a[1][1] * s(2,2,3,3,2,3,3,2) - c * s(2,1,3,3,2,3,3,1) + a[1][3] * s(2,1,3,2,2,2,3,1); res = max(res, ans2);
		int ans3 = a[1][1] * s(2,2,3,3,2,3,3,2) - a[1][2] * s(2,1,3,3,2,3,3,1) + c * s(2,1,3,2,2,2,3,1); res = max(res, ans3);
		int ans4 = -c * s(1,2,3,3,1,3,3,2) + a[2][2] * s(1,1,3,3,1,3,3,1) - a[2][3] * s(1,1,3,2,1,2,3,1); res = max(res, ans4);
		int ans5 = -a[2][1] * s(1,2,3,3,1,3,3,2) + c * s(1,1,3,3,1,3,3,1) - a[2][3] * s(1,1,3,2,1,2,3,1); res = max(res, ans5);
		int ans6 = -a[2][1] * s(1,2,3,3,1,3,3,2) + a[2][2] * s(1,1,3,3,1,3,3,1) - c * s(1,1,3,2,1,2,3,1); res = max(res, ans6);
		int ans7 = c * s(1,2,2,3,1,3,2,2) - a[3][2] * s(1,1,2,3,2,1,1,3) + a[3][3] * s(1,1,2,2,1,2,2,1); res = max(res, ans7);
		int ans8 = a[3][1] * s(1,2,2,3,1,3,2,2) - c * s(1,1,2,3,2,1,1,3) + a[3][3] * s(1,1,2,2,1,2,2,1); res = max(res, ans8);
		int ans9 = a[3][1] * s(1,2,2,3,1,3,2,2) - a[3][2] * s(1,1,2,3,2,1,1,3) + c * s(1,1,2,2,1,2,2,1); res = max(res, ans9);
		cout << res << endl;
	}
}
int main () {
	int t, n, c;
	cin >> t;
	while (t--) {
		cin >> n >> c;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				cin >> a[i][j];
			}
		}
		solve (n, c);
	}
	
	
	return 0;
}

然后发现被标答薄纱

#include <bits/stdc++.h>
using namespace std;
int a[4][4];

int calc(int n) {
	if (n == 1) return a[1][1];
	if (n == 2) return a[1][1] * a[2][2] - a[1][2] * a[2][1];
	return a[1][1] * a[2][2] * a[3][3] + a[1][2] * a[2][3] * a[3][1] + a[1][3] * a[2][1] * a[3][2] - a[1][3] * a[2][2] * a[3][1] - a[1][2] * a[2][1] * a[3][3] - a[1][1] * a[2][3] * a[3][2];
}
int main() {
	int t, n, c;
	scanf("%d", &t);
	while (t--) {
		scanf("%d%d", &n, &c);
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) scanf("%d", &a[i][j]);
		}
		int mx = -1e9;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				int tmp = a[i][j];
				a[i][j] = c;
				mx = max(mx, calc(n));
				a[i][j] = tmp;
			}
		}
		printf("%d\n", mx);
	}
	return 0;
}


E.

第二简单的签到,打印图形问题

#include <bits/stdc++.h>
using namespace std;
char a[10100][10100];
int main () {
	string s;
	cin >> s;
	int len = s.length();
	for (int i = 1; i <= len; i++) {
		char ch = s[len - i];
		if (ch == '0') {
			for (int j = 1; j <= len - i + 2; j++) a[i][j] = '*', a[2 * len - i][j] = '*';
		}
		else {
			for (int j = 1; j <= len - i + 2; j++) a[i][j] = '-', a[2 * len - i][j] = '-';
		}
		for (int j = len - i + 3; j <= len + 1; j++) {
				if (a[i - 1][j] == '-' || a[i - 1][j] == '|') a[i][j] = '|', a[2 * len - i][j] = '|';
				else a[i][j] = '*', a[2 * len - i][j] = '*';
 			}
	}
	for (int i = 1; i <= 2 * len - 1; i++) {
		for (int j = 1; j <= len + 1; j++) {
			cout << a[i][j];
		}
		cout << endl;
	}
	
	return 0;
}

D:

一眼dz,感觉是一道签到题
结果是一道思维题,我的贪心贪错了,
但是我的贪心正确率很高,导致我空想根本找不出来什么反例,
后来对拍了一下才找到了一组反例,然后又换了一个方法切掉了,感觉难度在M题附近浮动
我的思路:
先读取数据,判断是不是无穷(左右都至少要有一个0才不是无穷)
再对于左边快速排序一下,升序考虑每一条边
经过排序,左边即红色法术的法力消耗是递增的,依次遍历,对于第i张法术,我们把它的红色法力值作为答案红色法力值的备选,它的红色法术消耗大于上面所有法术,那么还要保证前面的法术无法使用,那么答案的蓝色法术值一定要小于前面所有法术的蓝色法术值,因为我们是遍历的,所以可以逐步更新维护蓝色法术值的最小值,直到找到一张蓝色法术值为0的法术(我们知道它一定存在),那么后面的法术值都不能作为答案了,就可以跳出循环。

#include <bits/stdc++.h>
using namespace std;
pair<int, int> a[5010];
bool compare(pair<int, int> a, pair<int, int> b) {
	if (a.first == b. first) return a.second < b.second; 
	return a.first < b.first;
}
int main () {
	int t; cin >> t;
	while(t--) {
		int ok1 = 1, ok2 = 1;
		int n, res;
		cin >> n;
		for (int i = 1; i <= n; i++) {
			cin >> a[i].first >> a[i].second;
			if (a[i].first == 0) ok1 = 0;
			if (a[i].second == 0) ok2 = 0;
		}
		if (ok1 || ok2) cout << "INF" <<endl;
		else {
			sort(a + 1, a + n + 1, compare);
			int minn = a[1].second;
			res = a[1].first + a[1].second - 2;
			for (int i = 1; i <= n; i++) {
				res = max(res, a[i].first + minn - 2);
				minn = min(minn, a[i].second);
				if (minn == 0) break;
			}
			 cout << res << endl;
		}
	}
	return 0; 
}

标答:

#include<bits/stdc++.h>
using namespace std;
int n;
struct node
{
	int x,y;
}a[5010];
bool cmp(node _1,node _2)
{
	if(_1.x!=_2.x)
		return _1.x<_2.x;
	return _1.y<_2.y;
}
int main()
{
		cin >> n;
		for(int i=1;i<=n;i++)
			cin >> a[i].x >> a[i].y;
		bool ok1=0,ok2=0;
		for(int i=1;i<=n;i++)
		{
			if(a[i].y==0)
				ok1=1;
			if(a[i].x==0)
				ok2=1;
		}
		if(!ok1||!ok2)
		{
			cout << "INF" << endl;
		}
		int small=1000000000;
		int ans=0;
		sort(a+1,a+1+n,cmp);
		for(int i=1;i<=n;i++)
		{
			small=min(small,a[i].y);
			if(small!=0) 
				ans=max(ans,small-1+a[i+1].x-1);
		}
		cout << ans << endl;	
	return 0;
} 

L:

纯思维题,因为要询问中位数,如果把1和n交换位置,不会影响中位数的选择,就是说这两种全排列都符合。
特判n==1时是唯一确定的。

#include <bits/stdc++.h>
using namespace std;
int main () {
	int n, m;
	cin >> n >> m;
	if (n == 1) cout << "YES" << endl;
	else cout << "NO" << endl;
	return 0;
}

H:

计算几何(也许?),题本身不难,但是有笨蛋把两点式方程算错了导致WA了很久~
算角度用余弦定理,有时候因为浮点数精度问题会导致cos值大于1或者小于-1,这时候acos函数会返回nan,我们需要将范围外的余弦置为1或-1
然后大约有怎么几种情况:

1.守门员划水,一点都没挡住。
2.前锋打假赛,不可能进。
3.守门员防守范围和射门范围之间有重合,但不是包含关系。
4.守门员防守范围包含于射门范围之内。

根据这四点分类讨论即可,
因为数据最大1e7,所以开了long double,不知道有没有用。

#include <bits/stdc++.h>
#define double long double 
using namespace std;
double dis (double x1, double y1, double x2, double y2) {
	return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
} 
double k (double x1, double y1, double x2, double y2) {
	if (x1 == x2) return x1;
	else if (y1 == y2) return 1;
	else return (x2 * y1 - x1 * y2) / (y1 - y2);
}
int main () {
	int t; cin >> t;
	double  w, x1, y1, x2, y2, d;
	while (t--) {
		cin >> w >> x1 >> y1 >> x2 >> y2 >> d;
		double d1 = dis(x1, y1, -w, 0), d2 = dis(x1, y1, w, 0), d3 = dis(x1, y1, x2 - d, y2), d4 = dis(x1, y1, x2 + d, y2);
		double k3 = k(x1, y1, x2 - d, y2), k4 = k(x1, y1, x2 + d, y2);
		double res1 = (d1 + d2 - 4 * w * w) / sqrt(d1 * d2) * 0.5, res2 = (d3 + d4 - 4 * d * d) / sqrt(d3 * d4) * 0.5;
		if (res1 > 1) res1 = 1.0;
		if (res1 < -1) res1 = -1.0;
		if (res2 > 1) res2 = 1.0;
		if (res2 < -1) res2 = -1.0;
		double ans1 = acos(res1), ans2 = acos(res2); 
	if (y1 < y2 || (y1 == y2 && (x2 - d > x1 || x2 + d < x1))) {
		printf("%.10Lf\n", ans1);
	}	
	else if (y1 == y2 || (k4 >= w && k3 <= -w)) printf("%.10lf\n", 0.0);
	else if (k3 >= w || k4 <= -w) {
		printf("%.10Lf\n", ans1);
	}
	else if (k4 >= w && k3 <= w && k3 >= -w) {
		d4 = d2;
		double d5 = dis(x2 - d, y2, w, 0);
	 	res2 = (d3 + d4 - d5) / sqrt(d3 * d4) * 0.5;
	 	if (res2 > 1) res2 = 1.0;
		if (res2 < -1) res2 = -1.0;
		ans2 = acos(res2);
		printf("%.10Lf\n", ans1 - ans2);
	}
	else if (k3 <= -w && k4 >= -w && k4 <= w) {
		d3 = d1;
		double d5 = dis(x2 + d, y2, -w, 0);
		res2 = (d3 + d4 - d5) / sqrt(d3 * d4) * 0.5;
		if (res2 > 1) res2 = 1.0;
		if (res2 < -1) res2 = -1.0;
		ans2 = acos(res2);
		printf("%.10Lf\n", ans1 - ans2);
	}
	else printf("%.10Lf\n", ans1 - ans2);	
	}
	return 0;
}

I:

本题还行,需要进行一些思考:
反正我就硬贪心,贪就完了!
分为这几种情况:
1.没车,全部走路。
2.车比人多,全部骑车。
3.有车且车比人少:
根据一个简单的贪心,取得答案时每个人骑车的时间,路程一定要相等,因此我们可以找到单位人骑车和走路的路程之比,
如果m % n == 0, 那么总共有为m / n 批车, 每人可以骑1批车, 骑车时间与总路程之比为 n : m
思考一下之后, m % n != 0时也是这个比例(我感觉不太好表述就不证了)
因此我们就可以得到结果
想明白以后程序挺简单的

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main () {
	ll t, n, m, l, v1, v2;
	cin >> t;
	long double ans;
	while (t --) {
		cin >> m >> n >> l >> v1 >> v2;
		if (m <= n) {
			ans = (long double)l / v2;
			printf("%.7Lf\n", ans);
	}
		else {
			if (n == 0) {
			ans =  (long double) l / v1;
			printf("%.7Lf\n", ans);
	}
			else  {
				ans = (long double) l / m * n / v2 + (long double) l / m * (m - n) / v1;
				printf("%.7Lf\n", ans);
			
		}
}}
	return 0;
}

G:

中规中矩的dp,我直接用状态机做了,
状态有不持有bit币和持有bit币两种
要达到最大收益,xzm学长每次交易都要交易1bit
定义一个f[i][j][k]:
经历i个时刻交易j次,是否持有股票时的收益
写一下状态转移:

初始状态:
f[0][j][0], f[0][j][1] 不可能存在,因为没有经历过时刻就交易过了,不能转移,设为负无穷
f[0][0][0] = 0
目标:
max(a[i][j][0])

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define INF 0x3f3f3f3f
double a[1010];
double f[1010][1010][2];
int main () {
	int n, k;
	cin >> n >> k;
	rep(i, 1, n) cin >> a[i];
	double ans = 0;
	rep(j, 0, k) f[0][j][0] = f[0][j][1] = -INF;
	f[0][0][0] = 0;
	rep(i, 1, n) {
		rep(j, 0, k) {
			f[i][j][0] = f[i - 1][j][0];
			f[i][j][1] = f[i - 1][j][1];
			if (j) {
				f[i][j][0] = max(f[i][j][0], f[i - 1][j - 1][1] + a[i]);
				f[i][j][1] = max(f[i][j][1], f[i - 1][j - 1][0] - a[i]);
			}
			ans = max(ans, f[i][j][0]);
		}
	}
	printf("%.2lf\n", ans);
	return 0;
}

B

数组模拟不太会,OPT看错方法了,写了一个错的deque,然后感觉不太会,就去看题解去了。
FIFO: 队列的性质,直接开一个队列执行操作即可。

rep(i, 1, n) {
		cin >> a[i];
		if (!v1[a[i]]) {
			cnt1 ++;
			v1[a[i]] = 1;
			if (q1.size() && q1.size() == k) {
				v1[q1.front()] = 0; q1.pop(); q1.push(a[i]); 
			}
			else q1.push(a[i]);
		}
	}

LRU:
淘汰最长时间未访问的界面,可以用一个经过修改的队列:
如果有一个元素即将入列且队列里面有这个元素,那么它的访问时间是需要更新的,但是又不想去交换顺序什么的,操作过于麻烦,就可以把这个元素直接入队,并打上标记,并把k++,即让队列最大长度加一,当要执行出队操作时,把队前有标记的元素直接出队,k--,去掉标记。

memset(v2, 0, sizeof v2);
	memset(v3, 0, sizeof v3);
	int k2 = k;
	rep(i, 1, n) {
		if (!v2[a[i]]) {
			cnt2 ++;
			v2[a[i]] = 1; 
			if (q2.size() && q2.size() >= k) {
			//	cout << k << "1" << endl;
				while (q2.size() && v3[q2.front()]) {
					k--; v3[q2.front()]--; q2.pop();
				}
				v2[q2.front()] = 0; q2.pop(); q2.push(a[i]); 
			}
			else q2.push(a[i]);
		}
		else {
			k++;
			q2.push(a[i]);
			v3[a[i]] ++;
		}
	}
	k = k2;	

OPT不会用队列做,deque也不会,题解是用数组模拟写的,还没看懂
总之,可以得到结论OPT永远是最少的。

F:

(脏牧狂喜)
背包
因为随从数量较少,可以单独对每一个随从讨论,让它成为狂乱攻击对象,攻击一个随从就要杀死它,否则没有意义,我们就可以把被攻击的随从的攻击力乘上被消灭所需要的攻击次数,就是消灭这个随从对于攻击随从的伤害。
把这个更新完以后,就是个01背包,直接做就行

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define lowbit(x) (x) & (-x)
#define fs first
#define sc second
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i  = n; i >= a; i--)
#define PII pair<int,int>
int n;
struct node{
	int v;
	int c;
}a[1010];
int f[10010], x[1010], y[1010];
bool compare (node a, node b) {
	return a.v < b.v;
}
int main () {
	cin >> n;
	rep(i, 1, n) cin >> x[i] >> y[i];
	int res = 0;
	rep(i, 1, n) {
		int sumc = 0, sumv = 0, m = 0;
		rep(j, 1, n) {
			if (i == j) continue;
			m++;
			a[m].v = x[j];
			a[m].c = y[j] % x[i] == 0 ? y[j] / x[i] : y[j] / x[i] + 1;
			a[m].c *= x[j];
			sumc += a[m].c;
			sumv += a[m].v;
		}
		if (sumc < y[i]) {
			res = max(res, sumv);
			continue;
		}
		sort(a + 1, a + n + 1, compare);
		memset(f, 0, sizeof f);
		int ans = 0;
		rep(k, 1, m) {
			rep(j, a[k].c - a[k]. v, y[i]) {
				int p = j - a[k].c + a[k].v;
				if (p >= 0) ans = max(ans, f[p] + a[k].v);
			}
			per(j, a[k].c, y[i] - 1) f[j] = max(f[j], f[j - a[k].c] + a[k]. v);
		}
		ans = max(ans, f[y[i] - 1]);
		ans += x[i];
		res = max(res, ans);
	}
	rep(i, 1, n) res -= x[i];
	cout << -res << endl;
	return 0;
}

计划写完F就不写了,C是一个容易TLE的大模拟,K是一个不好转化的数论.

posted @ 2022-05-06 22:42  misasteria  阅读(39)  评论(0编辑  收藏  举报