20201201模拟

T1

problem

\(n\)个石子,两个人选,每个人可以选\(a\)个或\(b\)个,问是否存在先手必胜的策略

solution

  • \(n \mod (a+b) < min(a,b)\)时,当我先手选\(a\),后手可以选\(b\),先手选\(b\),后手可以选\(a\),可以保证最后一次后手选完后剩下的石子\(<min(a,b)\),后手必胜

  • \(n \mod (a+b) \geq max(a,b)\)时,先手选\(max(a,b)\),那么现在的石子个数\(n' \mod (a+b) < min(a,b)\),后手变成了我第一种情况的先手,先手必胜

  • 在上述两种情况之间时设\(x = n \mod (a+b)\),可知\(min(a,b)\leq x < max(a,b)\),说明最后我无法选\(max(a,b)\),此时两个人轮流 选\(min(a,b)\),

    \(x/min(a,b)\)为奇数时,先手必胜

    \(x/min(a,b)\)为偶数时,后手必胜

code

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#define int long long
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
int t,n,a,b;
signed main(){
	freopen("nim.in","r",stdin);
	freopen("nim.out","w",stdout);
	t = read();
	while (t--){
		n = read(),a = read(),b = read();
		int x = n%(a+b);
		if (a > b) swap(a,b);
		if (x < a) {printf("0\n");continue;}
		if (x >= b) {printf("1\n");continue;}
		if ((x/a)%2 == 0) printf("0\n");
		else printf("1\n");
	}
	return 0;
}

T2

problem

每次往车中放入可放石子中最大的一个,直到无法装下任何一个为止,至多运\(k\)次,求最小装载量

solution

很容易想到二分,不过正确性未知,而且我写的混乱的check函数就离谱,就挂了……

code

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#define int long long
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
const int maxn = 2005;
int n,k;
int a[maxn];
bool vis[maxn];
bool check(int w){
	int j;memset(vis,0,sizeof(vis));
	for (int i = 1;i <= k;i++){
		int res = w;
		for (j = n;j >= 1;j--){
			if (vis[j]) continue;
			if (res-a[j] >= 0) res-=a[j],vis[j] = 1;
			else break;
		}
	}
	if (j == 0) return true;
	else return false;
}
signed main(){
	freopen("pack.in","r",stdin);
	freopen("pack.out","w",stdout);
	n = read(),k = read();
	int l,r = 0;
	for (int i = 1;i <= n;i++) a[i] = read(),r += a[i];
	sort(a+1,a+n+1);l = a[n];
	while (l < r){
		int mid = (l+r)>>1;
		if (check(mid)) r = mid;
		else l = mid+1;
	}
	printf("%lld\n",r);
	return 0;
}

T3

problem

\(n\)堆石子,从现有的石堆中各取一个形成新的石堆,问\(k\)次操作后石堆的情况

solution

每次只有最小的石堆会消失,优先队列维护一下就好了,同时新形成的石堆要加上他形成的那一天的天数,这样才能统一起跑线,使得石子减少的量等于总天数

code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
const int maxn = 1e5+10;
priority_queue<int> q;
int ans[maxn];
int n,k;
int main(){
	freopen("pile.in","r",stdin);
	freopen("pile.out","w",stdout);
	n = read(),k = read();
	for (int i = 1;i <= n;i++){
		int x = read();q.push(-x);
	}
	for (int i = 1;i <= k;i++){
		int x = q.size();q.push(-(x+i));
		while (q.top()+i >= 0) q.pop();
	}
	int i;
	for (i = 1;!q.empty();i++){
		ans[i] = -q.top();q.pop();
	}i--;
	printf("%d\n",i);
	while (i) printf("%d ",ans[i]-k),i--;
	return 0;
}
posted @ 2020-12-01 17:13  小又又yyyy  阅读(106)  评论(0编辑  收藏  举报