[日常训练]游戏题
ban
Description
n种英雄站成一列,要求每时每刻满足\(a_i\leq{a_{i+1}}\).
两人轮流操作,每次可以ban任意多人人.
问先手时候必胜.
HINT
\(n\leq10^5\).
Solution
这个条件有点麻烦...
然后把数列差分一下,\(d_i=a_i-a_{i-1}\),发现ban第i种英雄k个等价于\(d_i-k,d_{i+1}+k\),这就转化成了阶梯Nim了.
#define N 100005
int a[N],n,t,ans;
inline void Aireen(){
t=read();
while(t--){
n=read();
for(int i=1;i<=n;++i)
a[i]=read();
ans=0;
for(int i=n;i>=1;i-=2)
ans^=(a[i]-a[i-1]);
ans?puts("HH"):puts("GG");
}
}
light
Description
给你一个\(n\times{n}\)的网格及每个网格中灯初始开关状态.
可以对格子\((x,y)\)进行操作:改变\((x,y)\)及其上下左右四个格子的开关状态.
求一种方案使得灯全关掉.
HINT
\(n^2\leq500\).
Solution
方法一
枚举第一行的操作状态,O(nm)判是否可行.
#define N 25
int a[N][N],b[N][N],n;
bool flag;
inline bool chk(){
for(int i=1;i<n;++i){
for(int j=1;j<=n;++j){
if(a[i][j]^b[i][j]^b[i-1][j]^b[i][j-1]^b[i][j+1]) b[i+1][j]=1;
else b[i+1][j]=0;
}
}
for(int j=1;j<=n;++j)
if(a[n][j]^b[n][j]^b[n][j-1]^b[n-1][j]^b[n][j+1]) return false;
return true;
}
inline void dfs(int u){
if(flag) return;
if(u>n){
if(chk()){
flag=true;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(b[i][j]) printf("%d %d\n",i,j);
puts("0 0");
}
return;
}
b[1][u]=0;dfs(u+1);
b[1][u]=1;dfs(u+1);
}
inline void Aireen(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
scanf("%d",&a[i][j]);
dfs(1);
}
方法二
将状态压成二进制判断.
#define N 25
int a[N],b[N],n;
bool flag;
inline bool chk(){
for(int i=1;i<n;++i)
b[i+1]=(a[i]^b[i-1]^b[i]^(b[i]<<1)^(b[i]>>1))&((1<<n)-1);
return !((a[n]^b[n-1]^b[n]^(b[n]<<1)^(b[n]>>1))&((1<<n)-1));
}
inline void dfs(int u,int k){
if(flag) return;
if(u>n){
b[1]=k;
if(chk()){
flag=true;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(b[i]&(1<<j-1)) printf("%d %d\n",i,j);
puts("0 0");
}
return;
}
dfs(u+1,k<<1);
dfs(u+1,k<<1|1);
}
inline void Aireen(){
n=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(read()) a[i]|=1<<j-1;
dfs(1,0);
}
方法三
\(b_{i,j}\)表示\((i,j)\)的操作状态,则达成目标状态需要满足\(a_{i,j}\;xor\;b_{i,j}\;xor\;b_{i-1,j}\;xor\;b_{i+1,j}\;xor\;b_{i,j-1}\;xor\;b_{i,j+1}=0\).
解方程组即可.
#define N 25
#define M 505
int a[N][N],b[N][N],f[M][M],ans[M],n,cnt;
inline void gauss(int n){
for(int i=1;i<n;++i){
if(!f[i][i]){
for(int j=i;j<n;++j)
if(f[j][i]){
for(int k=i;k<=n;++k)
swap(f[i][k],f[j][k]);
}
}
for(int j=1;j<n;++j)
if(i!=j&&f[j][i]){
for(int k=i;k<=n;++k)
f[j][k]^=f[i][k];
}
}
for(int i=1;i<n;++i)
ans[i]=f[i][n];
}
inline void Aireen(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
b[i][j]=++cnt;
for(int i=1,k;i<=n;++i)
for(int j=1;j<=n;++j){
k=b[i][j];
f[k][k]=f[k][b[i][j-1]]=f[k][b[i-1][j]]\
=f[k][b[i][j+1]]=f[k][b[i+1][j]]=1;
f[k][cnt+1]=a[i][j];
}
gauss(cnt+1);
for(int i=1,k;i<=n;++i)
for(int j=1;j<=n;++j)
if(ans[b[i][j]])
printf("%d %d\n",i,j);
puts("0 0");
}
ppt
这道题真是$@#^#%...
两头往中间匹配:
尽量赢:小的尽量匹配比其小的,大的尽量匹配比其小的;不行的时候,用小的去换对方大的.
尽量输:小的尽量匹配比其大的,大的尽量匹配比其大的;不行的时候,用大的去换对方小的.
#define N 200005
int a[N],b[N],n,ans;
inline void Aireen(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
for(int i=1;i<=n;++i)
scanf("%d",&b[i]);
sort(a+1,a+1+n);sort(b+1,b+1+n);
for(int l=1,r=n,p=1,q=n;l<=r&&p<=q;){
while(a[l]>b[p]&&l<=r&&p<=q) ++l,++p,ans+=2;
while(a[r]>b[q]&&l<=r&&p<=q) --r,--q,ans+=2;
if(l>r||p>q) break;
ans+=(a[l]==b[q]);++l;--q;
}
printf("%d ",ans);
ans=0;
for(int l=1,r=n,p=1,q=n;l<=r&&p<=q;){
while(a[l]<b[p]&&l<=r&&p<=q) ++l,++p;
while(a[r]<b[q]&&l<=r&&p<=q) --r,--q;
if(l>r||p>q) break;
ans+=2-(a[r]==b[p]);--r;++p;
}
printf("%d\n",ans);
}
2017-10-25 21:02:55