【题解】弃疗Nim (2019,5.23)
Description
Sample Input
5 1
2 4 2 2
Sample Output
1
1
0
1
0
Solution
这题可以直接考虑暴力,时限也给了 3s 那么简直是给暴力一条活路啊。
开一个二维数组 \(CanBeGet[i][j]\) 表示石子个数在 \(i\) 时可以取走 \(j\) 个
暴力直接 \(O(N^3)\) 暴力把 \(CanBeGet\) 更新(Xor)一下。
最后统计答案的时候只要看看当前状态转移出去的是否有必输状态,有的话当前状态为胜。
细节就是把取一个也给附上初值。
我们考虑怎么优化这个鬼东西。
我们发现他要给一段区间 Xor 。
跟位运算有关系的东西我们都得想想状压,但是发现这题数据范围有 1e5 。。。。
那只能用bitset了。
我们记一个bitset,假设叫 \(f[i]\) ,表示当前石子个数能否取到剩下 \(i\) 。
这样每次变成一个新状态时就是要把之前的bitset左移一格,表示可以取的石子个数没变,但是剩下的石子数量变多了。
同时我们的这个bitset还要异或上当前这个石子堆大小可以取得区间的bitset。
可以取得区间就是区间全部为一的区间,可以用bitset快速做。
最后只要判断一下后继是否可以取到负的局面。
我用 \(ans[i]\) 保存答案。 如果 \(ans[i]==1\) 那么这就是必败的。
所以只要判断后继是否有可以取到的,同时满足是 \(ans\) 为 \(1\) ,那么当前状态就是必胜态,只有当后继没有必败态是才是败态。
骗分代码参考 \(subtask1\)
暴力代码参考 \(subtask2\)
正解代码参考 \(subtask3\)
#include<bits/stdc++.h>
using namespace std;
#define A first
#define B second
typedef pair<int,int> PII;
int n,m;
const int N=1005;
int SG[N],CanBeGet[N][N];
namespace subtask1{
inline void solve(){
for(int i=1;i<=n;++i) printf("%d\n",i&1);
}
}
namespace subtask2{
inline void solve(){
for(int i=1;i<=m;++i){
int a=0,b=0,c=0,d=0;
scanf("%d%d%d%d",&a,&b,&c,&d);
for(int j=a;j<=b;++j) for(int k=c;k<=d;++k)
CanBeGet[j][k]^=1;
}
for(int i=1;i<=n;++i) CanBeGet[i][1]=1;
for(int i=1;i<=n;++i) for(int j=1;j<=min(i,N-5);++j)
if(CanBeGet[i][j]){
if(i==j) SG[i]=1;
else SG[i]|=(!SG[i-j]);
}
for(int i=1;i<=n;++i) printf("%d\n",SG[i]);
}
}
namespace subtask3{
const int M=1e5+5;
vector <PII> v[M];
bitset<M> f,ans,BASE;
inline bitset<M> paint(int l,int r){
BASE.set();
BASE<<=l;
BASE^=BASE<<(r-l+1);
return BASE;
}
inline void solve(){
for(int i=1;i<=m;++i){
int a=0,b=0,c=0,d=0;
scanf("%d%d%d%d",&a,&b,&c,&d);
v[a].push_back(make_pair(c,d));
v[b+1].push_back(make_pair(c,d));
}
ans[0]=1;
// f[i] 表示从当前的石子个数可以转移到石子个数为i的石子状态
for(int i=1;i<=n;++i){
f<<=1;f[i-1]=1;// 可以取得石子变多了,那么我们能剩下的就变多了,刚好比之前多1,
for(int j=0;j<(int)v[i].size();++j) f^=paint(i-v[i][j].B,i-v[i][j].A);// 取出一段为1的区间
if(!(f&ans).count()) ans[i]=1;
}
for(int i=1;i<=n;++i) printf("%d\n",~ans[i]);
}
}
int main(){
scanf("%d%d",&n,&m);
if(m==0) return subtask1::solve(),0;
if(n<=1000) return subtask2:: solve(),0;
else subtask3::solve();
return 0;
}