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;
}