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
");continue;}
		if (x >= b) {printf("1
");continue;}
		if ((x/a)%2 == 0) printf("0
");
		else printf("1
");
	}
	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
",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
",i);
	while (i) printf("%d ",ans[i]-k),i--;
	return 0;
}
原文地址:https://www.cnblogs.com/little-uu/p/14069489.html