結果
| 問題 |
No.936 Are
|
| コンテスト | |
| ユーザー |
otamay6
|
| 提出日時 | 2019-12-01 13:40:23 |
| 言語 | C++14 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
WA
|
| 実行時間 | - |
| コード長 | 6,103 bytes |
| コンパイル時間 | 1,724 ms |
| コンパイル使用メモリ | 179,160 KB |
| 実行使用メモリ | 6,824 KB |
| 最終ジャッジ日時 | 2024-11-21 04:51:40 |
| 合計ジャッジ時間 | 15,998 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge1 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 3 WA * 22 |
ソースコード
#include"bits/stdc++.h"
#define REP(i,n) for(int i=0,i##_len=(n);i<i##_len;++i)
#define rep(i,a,b) for(int i=int(a);i<int(b);++i)
#define All(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
constexpr ll mod = 1e9+7;
bool lose(int l,int r){
return (l%5==0)&&(r%5==0);
}
void solve(){
int N,K;
cin>>N>>K;
int L[2],R[2];
cin>>L[0]>>R[0]>>L[1]>>R[1];
ll dp[2][6][6][6][6]={};
ll ans=0;
dp[N][L[1]][R[1]][L[0]][R[0]]=1;
REP(i,K+1){
REP(a,6) REP(b,6) REP(c,6) REP(d,6){//a,b:高橋の左右、c,d:lotの左右
if((a==0&&b==0)||(c==0&&d==0)) continue;
ll now=dp[N][a][b][c][d];
if(N&1){
if(lose(a+c,d)||lose(b+c,d)||lose(c,a+d)||lose(c,b+d)) continue;//勝ち確定なので考えなくていい
bool divible=false;
REP(l,6){//詰んでないときは常に分割できる
int r=a+b-l;
if(r<0) continue;
if(r>5) r-=5;
if(lose(l,r)||lose(l+c,r)||lose(l+d,r)||lose(l,r+c)||lose(l,r+d)) continue;//分割した結果負けるのはダメ
if(r==a&&l==b) continue;//分割の結果入れ替わるのはダメ
if(l==a&&r==b) continue;//分割の結果変わらないのはダメ
divible=true;
(dp[0][l][r][c][d]+=now)%=mod;
if(l!=0&&r==5){
divible=true;
(dp[0][l][0][c][d]+=now)%=mod;
}
}
bool attackable=false;
//行動の結果負けないなら攻撃してよい
if(a!=0){
if(!lose(a+d,b)&&!lose(a+c+a,b)&&c!=0){
(dp[0][a][b][(a+c)%5][d]+=now)%=mod;
attackable=true;
}
if(!lose(a+c,b)&&!lose(a+d+a,b)&&d!=0){
(dp[0][a][b][c][(a+d)%5]+=now)%=mod;
attackable=true;
}
}
if(b!=0){
if(!lose(a,b+d)&&!lose(a,b+c+b)&&c!=0){
(dp[0][a][b][(b+c)%5][d]+=now)%=mod;
attackable=true;
}
if(!lose(a,b+c)&&!lose(a,b+d+b)&&d!=0){
(dp[0][a][b][c][(b+d)%5]+=now)%=mod;
attackable=true;
}
}
//攻撃しかできないし、どう攻撃しても詰んでるなら全ての行動を取る
if(!divible&&!attackable){
if(a!=0){
if(c!=0) (dp[0][a][b][(a+c)%5][d]+=now)%=mod;
if(d!=0) (dp[0][a][b][c][(a+d)%5]+=now)%=mod;
}
if(b!=0){
if(c!=0) (dp[0][a][b][(b+c)%5][d]+=now)%=mod;
if(d!=0) (dp[0][a][b][c][(b+d)%5]+=now)%=mod;
}
}
}
else{//lot君はあらゆる手を取る
if(c!=0){
if(a!=0) (dp[1][(a+c)%5][b][c][d]+=now)%=mod;
if(b!=0) (dp[1][a][(b+c)%5][c][d]+=now)%=mod;
}
if(d!=0){
if(a!=0) (dp[1][(a+d)%5][b][c][d]+=now)%=mod;
if(b!=0) (dp[1][a][(b+d)%5][c][d]+=now)%=mod;
}
REP(l,6){
int r=c+d-l;
if(r<0) continue;
if(r>5) r-=5;
if(l==0&&r==0) continue;
if(r==c&&l==d) continue;//分割の結果
if(l==c&&r==d) continue;//分割の結果変わらないのはダメ
(dp[1][a][b][l][r]+=now)%=mod;
if(l!=0&&r==5){
(dp[1][a][b][l][0]+=now)%=mod;
}
}
}
}
REP(a,6) REP(b,6) REP(c,6) REP(d,6){//lot君が勝つときを加算して使いまわすために初期化
if(a==0&&b==0) (ans+=dp[N][a][b][c][d])%=mod;
dp[N][a][b][c][d]=0;
}
N=1-N;
}
cout<<ans<<endl;
}
int main(){
solve();
}
/*
まず、分割できるパターンが存在すれば攻撃できないならする必要はないし、分割できるパターンが存在しなければ攻撃する必要がある
例えば高橋くんの手が(0,1)の場合は自明で、必ず攻撃することになる
それ以外、例えば(3,3,1,1)などの場合高橋君は自分の手を(0,2)にしたりはしない、これも分割できないパターンとして扱う
大事なのは分割できないパターンでも両手が共に1以上ならば攻撃するだけで次のターンに負けないということで、これを留意して場合分けを行う(もちろん片手が0でも分割によって両手を共に1にできる)
明らかに分割できるパターンでは、高橋君は次のターン負けないことができる、そのため、攻撃することで次のターン負けるようならその攻撃はしなくてもよい
分割できないパターンにおいては、以下のような場合分けが行われる
・もしどう攻撃しても自分の負けになるならばあらゆる全ての行動をとることになる、このとき、分割することで負けになるパターンは存在せず、攻撃行動だけを考えてよい
・そうでなくて、何れかの手を選ぶことによって次のターンで負けるならば、その行動は選ぶ必要がない
・つまり、詰んでるとき以外は分割できるパターンにおける攻撃行動と全く同じ行動を取ればいいことが分かる
以上のまとめにより場合分けを実装できる
具体的には、分割、攻撃によって次のターン負けないことができるならばその行動を選択するし、そのような行動がないならば全ての攻撃行動を試せばよい
*/
otamay6