題目翻譯 link
有 \(N\) 人圍坐在一張圓桌旁,按逆時針順序編號為 \(1\) 至 \(N\) 。每個人都有一個慣用手
圓桌上有 \(N\) 把勺子,編號為 \(1\) 到 \(N\) ,每對相鄰的人之間放一把勺子
給你一個 \((1, \dots, N)\) 的排列組合 \((P_1, \dots, P_N)\) 。在 \(i=1,\dots,N\) 的順序中,人 \(P_i\) 的行為如下:
- 如果兩邊都有剩餘的勺子,他們會拿自己慣用手一邊的勺子
- 如果左側或右側有剩餘的勺子,他們將拿走其中一個
- 否則,他們什麼也不會做
給出了一個長度為 \(N\) 的字串 \(S\) ,由 L
、R
和 ?
組成:
- 如果 \(S_i\) 是 "L",那麼 \(i\) 是左撇子
- 如果 \(S_i\) 是 "R",那麼 \(i\) 是右撇子
在 \(2^N\) 個可能的主手組合中,找出有多少個滿足以下所有條件的 \(s\),模數為 \(998244353\) :
當每個人都行動完後,每個人都拿了一把勺子
方法
法一
暴力列舉每種可能的 \(s\) ,然後檢驗
時間複雜度 \(O_{(2^X \times N)}\) , \(X\) 為 ?
的個數
法二
分析
我們可以發現對於一個滿足條件的 \(S\) ,每個人都應該拿與第一個拿的人同側的勺子
於是:
我們可以分類討論:
討論第一個拿的人是拿左邊還是右邊:
- 若為
R
只討論右邊 L
只討論左邊?
討論兩種最後求和
每個人拿的方向應與第一個拿的人一致,然後找規律
規律
(注:這裡類似 “ \(P_i\) 為 L
” 的語句其實是 “ \(S[P_i]\) 為 L
” ,為了簡便記作 \(P_i\) )
若第一個拿的人拿左邊
對於第 \(P_i\) 個人來說
- 如果他右邊的人 \(r\) 已經先拿過他左邊的勺子,那麼 \(P_i\) 可以為
L
或R
,那麼 \(ans\) 乘 \(2\) (注意,若 \(P_i\) 確定, \(ans\) 不變)- 如果他右邊的人 \(r\) 後拿,那麼他只能拿左邊的勺子, \(ans\) 不變
- 如果他右邊的人 \(r\) 沒有先拿過他左邊的勺子且 \(P_i=\)
R
,那麼第一個拿的人拿左邊的情況誤解,即 \(ans=0\)
若第一個拿的人拿右邊,可類似若第一個拿的人拿左邊推出
這裡為了簡短就不寫啦
注意取模!!!你猜我怎麼知道的 警示後人
程式碼實現:
#include<bits/stdc++.h>
#define put(n) scanf("%lld",&n)
#define out(n) printf("%lld\n",n)
#define int long long
#define fd(i,a,b) for(int i=a;i<=b;i=-~i)
using namespace std;
int n,p[1000100],f[1000100],mod=998244353;
//f記錄第i個人第幾個拿
char s[1000100];
int ans1=1,ans2=1;// 拿左邊 拿右邊
signed main()
{
put(n);
fd(i,1,n) put(p[i]),f[p[i]]=i;
fd(i,1,n) cin>>s[i];
//第一個拿的人拿左邊
fd(i,1,n)
{
int r=p[i]+1;//r 當前拿的人右邊的人的編號
if(r==n+1) r=1;//小坑
if(s[p[i]]=='?')
{
if(f[r]<i) ans1*=2;
ans1%=mod;
}
else if(s[p[i]]=='R')
{
if(f[r]>i)
{
i=n+1;
ans1=0;
break;
}
}
}
//第一個拿的人拿右邊
fd(i,1,n)
{
int l=p[i]-1;//l 當前拿的人左邊的人的編號
if(l==0) l=n;//小坑
if(s[p[i]]=='?')
{
if(f[l]<i) ans2*=2;
ans2%=mod;
}
else if(s[p[i]]=='L')
{
if(f[l]>i)
{
i=n+1;
ans2=0;
break;
}
}
}
if(s[p[1]]=='R') out(ans2);
else if(s[p[1]]=='L') out(ans1);
else out((ans1+ans2)%mod);//如果Pi為?那麼算兩種情況之和
return 0;
}
無關的話(稽核大大,求過審):被禁言了,怎麼解啊?
感謝觀看