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