CF1369F-BareLee【博弈論,SG函式】
阿新 • • 發佈:2022-03-24
正題
題目連結:https://www.luogu.com.cn/problem/CF1369F
題目大意
\(T\)次遊戲,每次給出一個\(s\)和\(t\),兩個人輪流操作,可以讓\(s=s+1\)或者\(s=s\times 2\),如果\(s>t\)的話那個人就輸了。
每次輸的人將作為下一次的先手,最後一把決定勝負。
求第一次先手的人是否有必勝/必敗策略。
\(1\leq s\leq t\leq 10^{18},1\leq T\leq 10^5\)
解題思路
先考慮一把裡面是否有必勝策略,考慮用\(SG\)函式去求,因為我們只需要維護\(SG\)為\(0\)和不為\(0\)的資訊就好了。
如果一個狀態不能走到任何\(0\)
首先\(\lfloor\frac{t}{2}\rfloor+1\sim t\)這個範圍中因為\(\times 2\)用不了,所以這個範圍肯定是\(01\)交錯的。
然後考慮從\(\frac{t}{2}\)開始往前每個狀態都會額外指向一個乘二後的值,如果也就是前面\(01\)交錯中每次跳兩個,所以要麼指向的都是\(0\)要麼指向的都是\(1\),如果指向的都是\(0\),那麼後面就有一段都是\(1\),否則如果指向的是\(1\)顯然不會產生影響,依舊是\(01\)交錯。
然後讓\(t\)繼續除二,然後考慮如果後面是連續\(1\)
然後考慮必敗,最後一步肯定是\(\times 2\),所以我們直接讓\(t=\lfloor\frac{t}{2}\rfloor\),然後看是否必勝,這就是能否必敗的答案了。
然後反過來\(dp\)一次就可以得到答案了。
時間複雜度:\(O(T\log t)\)
code
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll N=1e5+10; ll n,p[N][2],f[N][2]; bool check(ll s,ll t){ bool now=0,k=0;ll r=t; while(t/2>=s){ if(now)now=0,r=t/2; else now=k^!(t&1); t/=2;k=(r-t)&1; } return now|((r-s)&1); } signed main() { scanf("%lld",&n); for(ll i=1,s,t;i<=n;i++){ scanf("%lld%lld",&s,&t); p[i][0]=check(s,t); if(t/2>=s)p[i][1]=check(s,t/2); else p[i][1]=1; } f[n][0]=p[n][0];f[n][1]=p[n][1]; for(ll i=n-1;i>=1;i--){ f[i][0]=(f[i+1][0]&p[i][1])|((!f[i+1][0])&p[i][0]); f[i][1]=(f[i+1][1]&p[i][1])|((!f[i+1][1])&p[i][0]); } printf("%lld %lld\n",f[1][0],f[1][1]); return 0; }