300iq Contest 1 簡要題解
咕咕咕
A. Angle Beats
description
有一張\(n\times m\)的方陣,每個位置上標有*
,+
,.
中的一種。你要用盡可能多的\(I\)型和\(L\)型覆蓋這個方陣(均占恰好\(3\)個位置),要求\(I\)型中間那個位置必須是+
,\(L\)型中間那個位置必須是*
或者+
,兩者非中間的兩個位置必須都是.
。
\(1 \le n, m \le 100.\)
solution
由於某種原因,這道題咕咕咕了。
B. Best Subsequence
description
給出\(n,k\)以及\(\{w_1,w_2,...,w_n\}\),你需要找出一個長度為\(k\)的子序列\(1 \le i_1 < i_2 < ... < i_k \le n\),最小化\(\max\{w_{i_1}+w_{i_2},w_{i_2}+w_{i_3},...,w_{i_{k-1}}+w_{i_k},w_{i_k}+w_{i_1}\}\)。
\(3 \le k \le n \le 2\times 10^5, 1 \le w_i \le 10^9.\)
solution
假設答案是\(mid\),那么子序列中相鄰(首尾也算相鄰)兩個數中至少有一個不超過\(\lfloor\frac{mid}{2}\rfloor\)。
二分\(mid\),找出所有不超過\(\lfloor\frac{mid}{2}\rfloor\)的數,顯然這些樹構成的子序列是合法的,接着相鄰兩個數中至多插入一個新數,線性掃一遍統計即可。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2e5+5;
int n,k,w[N],p[N];
int check(int mid){
int cnt=0,res=0;
for(int i=1;i<=n;++i)if(w[i]<=mid/2)p[++cnt]=i;
if(cnt<=1)return cnt;
for(int i=1;i<cnt;++i)
for(int j=p[i]+1;j<p[i+1];++j)
if(w[j]+max(w[p[i]],w[p[i+1]])<=mid){
++res;break;
}
for(int j=p[cnt]+1;j<=n;++j)
if(w[j]+max(w[p[cnt]],w[p[1]])<=mid)
return cnt+res+1;
for(int j=1;j<p[1];++j)
if(w[j]+max(w[p[cnt]],w[p[1]])<=mid)
return cnt+res+1;
return cnt+res;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)scanf("%d",&w[i]);
int l=2,r=2e9,res;
while(l<=r){
int mid=l+(r-l>>1);
if(check(mid)>=k)res=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",res);return 0;
}
C. Cool Pairs
description
給出兩個\(n\)階排列\(p,q\)和一個參數\(k\),要求構造兩個序列\(a,b\)滿足:
- \(-n \le a_i, b_i \le n\)。
- \(a_{p_1} \le a_{p_2} \le ... \le a_{p_n}, b_{q_1} \le b_{q_2} \le ... \le b_{q_n}\)。
- 滿足\(i<j, a_i<b_j\)的\((i,j)\)數量恰好為\(k\)。
\(1 \le n \le 3\times 10^5, 0 \le k \le \frac{n(n-1)}{2}.\)
solution
按照如下方法構造:
- \(a_{p_i}=i-1-n\)。
- 存在一個\(x\in[1,n]\),使\(\forall i\in[1,x), b_{q_i}=0, \forall i\in(x,n], b_{q_i}=n\)。
可以發現這樣構造出來的滿足\(i<j, a_i<b_j\)的\((i,j)\)數量為\(\sum_{i=1}^{x-1}(q_i-1)+y\),其中\(y\)是一個與\(b_{q_x}\)取值有關的在\([0,q_x)\)范圍內的數。易證明這種構造方法可以對任意合法的\(k\)構造出解。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3e5+5;
int n,a[N],b[N],c[N];long long k;
int main(){
scanf("%d%lld",&n,&k);
for(int i=1,x;i<=n;++i)scanf("%d",&x),a[x]=i-1-n,b[i]=n;
for(int i=1,x;i<=n;++i){
scanf("%d",&x);
if(x-1<=k)b[x]=0,k-=x-1;
else{
for(int j=1;j<x;++j)c[j]=a[j];
sort(c+1,c+x);
b[x]=-c[k+1];break;
}
}
puts("Yes");
for(int i=1;i<=n;++i)printf("%d ",a[i]);puts("");
for(int i=1;i<=n;++i)printf("%d ",b[i]);puts("");
return 0;
}
D. Dates
description
在接下來的\(t\)天時間里,有\(n\)個女孩子想要和\(\text{y}\color{red}{\text{yb}}\) 約會。第\(i\)個女孩子想要在\([l_i,r_i]\)中的某一天和\(\text{y}\color{red}{\text{yb}}\)約會,且約會后\(\text{y}\color{red}{\text{yb}}\)會得到\(p_i\)的偷稅值。在這里,我們保證\(l_i\le l_{i+1}, r_i \le r_{i+1}\)。\(\text{y}\color{red}{\text{yb}}\)在第\(i\)天至多和\(a_i\)個女孩子約會,他希望你能幫他求出他能夠獲得的最大偷稅值之和。
\(1 \le n, t \le 3\times 10^5, 0 \le a_i \le n, 1 \le p_i \le 10^9.\)
solution
按照\(p_i\)從大到小依次加入區間,每次判斷加入后是否仍存在完美匹配,這種做法的正確性顯然。
根據\(Hall\)定理,判斷是否存在完美匹配,只需要判斷是否任意\(1 \le L \le R \le t\)均滿足\(\sum_{i=L}^Ra_i \ge \sum[l_i \ge L][r_i \le R]\)。
然后由於保證了\(l_i\le l_{i+1}, r_i \le r_{i+1}\),因而枚舉\(1 \le L \le R \le t\)可以替換為枚舉\(1 \le L' \le R' \le n\)即枚舉“區間的區間”。設\(b_i\in\{0,1\}\)表示第\(i\)個區間是否被選,那么需要滿足的限制就變成了:\(\sum_{i=L}^Rb_i\le \sum_{i=l_L}^{r_R}a_i\)。
記\(sa_i,sb_i\)分別為\(a_i\)和\(b_i\)的前綴和,那么限制為\(sb_R-sb_{L-1}\le sa_{r_R}-sa_{l_L-1}\)即\(sb_R-sa_{r_R}\le sb_{L-1}-sa_{l_L-1}\)。再記\(c_i=sb_i-sa_{r_i},d_i=sb_{i-1}-sa_{l_i-1}\),限制為\(c_R\le d_L\)。
考慮每次加入一個區間即企圖將某一個\(b_x\)由\(0\)改為\(1\),觀察發現只會有\(c_{x...n}\)與\(d_{1...x}\)的大小關系會受到影響,因而維護后綴\(c_i\)的最大值和前綴\(d_i\)的最小值判斷即可。
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
const int N=3e5+5;
struct segment_tree{
ll op,arr[N],val[N<<2],tag[N<<2];
ll merge(ll x,ll y){return op?max(x,y):min(x,y);}
void build(int x,int l,int r){
if(l==r){val[x]=arr[l];return;}
tag[x]=0;int mid=l+r>>1;
build(x<<1,l,mid);build(x<<1|1,mid+1,r);
val[x]=merge(val[x<<1],val[x<<1|1]);
}
void init(int n,ll _op){
op=_op;build(1,1,n);
}
void modify(int x,int l,int r,int ql,int qr,ll v){
if(l>=ql&&r<=qr){val[x]+=v;tag[x]+=v;return;}
int mid=l+r>>1;
if(ql<=mid)modify(x<<1,l,mid,ql,qr,v);
if(qr>mid)modify(x<<1|1,mid+1,r,ql,qr,v);
val[x]=merge(val[x<<1],val[x<<1|1])+tag[x];
}
ll query(int x,int l,int r,int ql,int qr){
if(l>=ql&&r<=qr)return val[x];
int mid=l+r>>1;
if(qr<=mid)return query(x<<1,l,mid,ql,qr)+tag[x];
if(ql>mid)return query(x<<1|1,mid+1,r,ql,qr)+tag[x];
return merge(query(x<<1,l,mid,ql,qr),query(x<<1|1,mid+1,r,ql,qr))+tag[x];
}
}T1,T2;
int n,m,p[N],id[N];ll sa[N],ans;
bool cmp(int x,int y){return p[x]>p[y];}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)scanf("%lld",&sa[i]),sa[i]+=sa[i-1];
for(int i=1,l,r;i<=n;++i){
scanf("%d%d%d",&l,&r,&p[i]);id[i]=i;
T1.arr[i]=-sa[r];T2.arr[i]=-sa[l-1];
}
T1.init(n,1);T2.init(n,0);sort(id+1,id+n+1,cmp);
for(int i=1;i<=n;++i)
if(T1.query(1,1,n,id[i],n)<T2.query(1,1,n,1,id[i])){
T1.modify(1,1,n,id[i],n,1);
if(id[i]<n)T2.modify(1,1,n,id[i]+1,n,1);
ans+=p[id[i]];
}
printf("%lld\n",ans);return 0;
}
E. Expected Value
description
平面圖上隨機游走(每次等概率走一條出邊),求從點\(1\)走到點\(n\)所需步數的期望模\(998244353\)。
\(2 \le n \le 3000.\)
solution
由於是平面圖因此邊數是\(O(n)\)的。
學習zzq的候選隊論文可以得到一個\(O(nm)\)解稀疏矩陣的做法,其中\(m\)是矩陣非零元素數量。
但是由於這種做法較為復雜繁瑣以致使人忍不住點擊右上角,因為以下提交了一份每次選取非零位最少的列向量消元的做法,然后它過了。
有沒有老鴿來證明(估計是不太行了)或證偽這玩意兒的復雜度鴨。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3005;
const int mod=998244353;
int n,m,a[N][N],cnt[N],pos[N],ans[N];
void Inc(int &x,int y){x+=y;x>=mod?x-=mod:x;}
int fastpow(int x,int y){
int res=1;
while(y){if(y&1)res=1ll*res*x%mod;x=1ll*x*x%mod;y>>=1;}
return res;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%*d%*d");
scanf("%d",&m);
for(int i=1,x,y;i<=m;++i){
scanf("%d%d",&x,&y);
Inc(a[x][x],mod-1);Inc(a[y][y],mod-1);
Inc(a[x][y],1);Inc(a[y][x],1);
Inc(a[x][n+1],1);Inc(a[y][n+1],1);
}
for(int i=1;i<=n+1;++i)a[n][i]=0;a[n][n]=1;
for(int i=1;i<=n;++i)
for(int j=1;j<=n+1;++j)
if(a[i][j])++cnt[i];
cnt[0]=1<<30;
for(int i=1;i<=n;++i){
int r=0;
for(int j=i;j<=n;++j)if(a[j][i]&&cnt[j]<cnt[r])r=j;
if(r!=i)swap(a[r],a[i]),swap(cnt[r],cnt[i]);
int inv=fastpow(a[i][i],mod-2),top=0;
for(int j=i;j<=n+1;++j){
a[i][j]=1ll*a[i][j]*inv%mod;
if(a[i][j])pos[++top]=j;
}
for(int j=i+1;j<=n;++j)
if(a[j][i]){
int t=mod-a[j][i];
for(int k=1;k<=top;++k){
if(a[j][pos[k]])--cnt[j];
a[j][pos[k]]=(a[j][pos[k]]+1ll*t*a[i][pos[k]])%mod;
if(a[j][pos[k]])++cnt[j];
}
}
}
for(int i=n;i;--i){
ans[i]=mod-a[i][n+1];
for(int j=i+1;j<=n;++j)
ans[i]=(ans[i]+1ll*(mod-ans[j])*a[i][j])%mod;
}
printf("%d\n",ans[1]);return 0;
}
F. Free Edges
description
給一張\(n\)點\(m\)條邊的圖,初始時每條邊都是白的,你需要選擇若干條邊染成黑色。
每當存在某一個點只有一條白色鄰邊時,這條白色鄰邊會變成黑色。
求出最少把多少條邊染成黑色后可以使所有邊都變成黑色。
$1 \le n, m \le 10^5. $
solution
其實就是問至少刪掉多少條邊后圖中不存在環。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int n,m,fa[N],ans;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)fa[i]=i;
for(int i=1,x,y;i<=m;++i){
scanf("%d%d",&x,&y);
if(find(x)!=find(y))fa[find(x)]=find(y);
else ++ans;
}
printf("%d\n",ans);return 0;
}
G.Graph Counting
description
給定\(n\),求有多少張\(2n\)個點的圖滿足不存在完美匹配,且加入任意一條不在圖中的邊后存在完美匹配。兩張圖被認為不同當且僅當無法通過重標號使兩張圖完全一致。答案對\(998244353\)取模。
\(1 \le n \le 5\times 10^5.\)
solution
翻題解抄了個結論:
一張圖滿足題述條件當且僅當存在若干點與其余所有點均有連邊,設這些點的數量為\(x\),將這些點全部刪去后,圖中剩下的部分是\(x+2\)個大小為奇數的團。
那么在枚舉了\(x\)后,答案等於將\(2n-x\)拆分成\(x+2\)個奇數的方案數,也等於將\(n+1\)拆分成\(x+2\)正整數的方案數。
對所有的\(x\)的答案求和,發現就是將\(n+1\)拆分成至少\(2\)個正整數的方案數,也就是\(P(n+1)-1\)。
所以只要求拆分數就行了。使用五邊形數定理可以做到\(O(n\sqrt n)\)的復雜度,由於常數較小,可以通過\(n \le 5\times 10^5\)的測試數據。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=5e5+5;
const int mod=998244353;
int n,m,f[N],g[N];
void Inc(int &x,int y){x+=y;x>=mod?x-=mod:x;}
int main(){
scanf("%d",&n);++n;
f[1]=1;f[2]=2;f[3]=5;f[m=4]=7;
while(f[m]<=n)++m,f[m]=3+2*f[m-2]-f[m-4];
for(int i=g[0]=1;i<=n;++i)
for(int j=1;f[j]<=i;++j)
if((j+1>>1)&1)Inc(g[i],g[i-f[j]]);
else Inc(g[i],mod-g[i-f[j]]);
printf("%d\n",(g[n]+mod-1)%mod);
return 0;
}
H. Hall's Theorem
description
對於一張二分圖,設\(A\)為二分圖左側的一個非空點集,定義\(N(A)\)表示\(A\)集合中所有點的相鄰點構成的集合的並,即,二分圖右側與\(A\)中至少一個點相鄰的點的集合。
定義一個\(A\)是不好的當且僅當\(|N(A)|<|A|\)。
給出\(n,k\),要求構造一張左右各\(n\)個點的二分圖,滿足恰有\(k\)個左側的非空點集是不好的。
\(1 \le n \le 20, 0 \le k < 2^n.\)
solution
出於某種原因,以下均討論好的點集的數量(我們認為非不好的點集就是好的點集)。
不妨將限制加強一些:構造一個數列\(\{a_i\}\),要求\(n \ge a_1 \ge a_2 \ge ... \ge a_n \ge n\),然后對於二分圖左側的第\(i\)個點,它向二分圖右側的前\(a_i\)個點連邊。
枚舉一個集合\(A\)中的最小元素\(x\),那么這個集合的\(N(A)\)就等於\(a_x\),為保證集合是好的,我們要求集合的大小不能超過\(N(A)\)也即\(a_x\),那么這種構造方式下好的點集的總量就是:$$\sum_{i=1}^n\sum_{j=0}^{a_i-1}\binom{n-i}{j}$$
按照\(i\)從小到大的順序貪心地構造\(a_i\)(這樣甚至都不需要考慮\(a_i\)內部的偏序關系了),可以證明這種方法可以對任意合法的\(k\)構造出方案。
#include<cstdio>
#include<algorithm>
using namespace std;
int n,k,c[23][23],m,a[666],b[666];
int main(){
scanf("%d%d",&n,&k);k=(1<<n)-1-k;
for(int i=c[0][0]=1;i<n;++i)
for(int j=c[i][0]=1;j<=i;++j)
c[i][j]=c[i-1][j]+c[i-1][j-1];
for(int i=n-1;~i;--i)
for(int j=0;j<=i;++j)
if(k>=c[i][j])k-=c[i][j],a[++m]=n-i,b[m]=j+1;
else break;
printf("%d\n",m);
for(int i=1;i<=m;++i)printf("%d %d\n",a[i],b[i]);
return 0;
}
I. Interesting Graph
給一張圖,滿足如下性質:
對於任意的大小為\(7\)的子集\(A\),都存在兩點\(a,b\in A\)以及一個點\(c \notin A\),滿足所有從\(a\)到\(b\)的路徑均包含點\(c\)。
For any subset \(A\) of \(7\) vertices of the graph, there are some two vertices \(a, b \in A\) and some vertex \(c \notin A\) such that all paths from \(a\) to \(b\) contain vertex \(c\).
求對這張圖進行\(k(k=1,2,...,n)\)染色的方案數模\(998244353\)。
\(1 \le n, m \le 10^5.\)
solution
性質表明圖中不存在大小大於等於\(7\)的連通塊。
對所有連通塊求出\(dp_i\)表示將該連通塊划分成\(i\)個集合的方案數,這部分的復雜度不超過\(O(n\times 3^6)\),然后合並不同連通塊時可以使用分治法法塔再套個多點求值發現\(6\)個點構成的本質不同的圖只有一百多種,所以將\(dp\)數組相同的合並后直接暴力就好了。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N=1e5+5;
const int mod=998244353;
int inv[10],C[10],n,m,mrk[N],tot,all,ban[100],dp[7][100],scc,baz[N];
vector<int>E[N],foo[N],bar[N];
inline void Inc(int &x,int y){x+=y;x>=mod?x-=mod:x;}
inline int fastpow(int x,int y){
int res=1;
while(y){if(y&1)res=1ll*res*x%mod;x=1ll*x*x%mod;y>>=1;}
return res;
}
void dfs(int u){
mrk[u]=tot++;
for(int v:E[u]){
if(!~mrk[v])dfs(v);
ban[1<<mrk[u]|1<<mrk[v]]=1;
}
}
int main(){
scanf("%d%d",&n,&m);inv[1]=1;
for(int i=2;i<10;++i)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
for(int i=1,x,y;i<=m;++i)scanf("%d%d",&x,&y),E[x].push_back(y),E[y].push_back(x);
memset(mrk,-1,sizeof(mrk));
for(int u=1;u<=n;++u)
if(!~mrk[u]){
memset(ban,0,sizeof(ban));tot=0;
dfs(u);all=(1<<tot)-1;
for(int i=1;i<=all;i<<=1)
for(int j=0;j<=all;++j)
if(j&i)
ban[j]|=ban[j^i];
memset(dp,0,sizeof(dp));dp[0][0]=1;
for(int i=0;i<tot;++i)
for(int j=0;j<=all;++j)
if(dp[i][j])
for(int s=all^j,k=s;k;k=(k-1)&s)
if(!ban[k])
Inc(dp[i+1][j|k],dp[i][j]);
vector<int>vec(tot);
for(int i=0;i<tot;++i)vec[i]=dp[i+1][all];
foo[++scc]=vec;
}
sort(foo+1,foo+scc+1);tot=0;
for(int i=1;i<=scc;++i){
if(i==1||foo[i]!=bar[tot])
bar[++tot]=foo[i];
++baz[tot];
}
for(int i=1;i<=n;++i){
C[0]=i;
for(int j=1;j<6;++j)C[j]=1ll*C[j-1]*(i-j+mod)%mod*inv[j+1]%mod;
int res=1;
for(int j=1;j<=tot;++j){
int s=0;
for(int k=0;k<bar[j].size();++k)
Inc(s,1ll*bar[j][k]*C[k]%mod);
res=1ll*res*fastpow(s,baz[j])%mod;
}
printf("%d ",res);
}
puts("");return 0;
}
J. Jealous Split
description
有一個非負整數序列\(\{a_i\}\),你要將他分成恰好\(k\)段,記\(s_i\)為第\(i\)段的和,\(m_i\)為第\(i\)段的最大值,你需要保證這種划分方案對任意\(1 \le i <k\)滿足\(|s_i-s_{i+1}|\le \max(m_i,m_{i+1})\)。
\(3 \le k \le n \le 10^5, 0 \le a_i \le 5\times 10^4.\)
solution
我們定義“對序列進行一次\(val\)分段”為(從前往后,從后往前)掃一遍序列,每當權值和大於等於\(val\)就分一段。二分出最大的\(x\)滿足對序列進行\(x\)分段后權值和大於等於\(x\)的段數大於等於\(k\),令其為\(mid\),假設對序列進行\(mid\)分段后序列被恰好分成了\(k\)段,且所有段並起來為\([1,n]\),那么可以證明這種划分方案在原問題下是合法的。
若不滿足,則可以對序列倒着做\(mid+1\)分段,通過某種方式(這里的證明咕咕咕了)可以證明正着做\(mid\)分段與倒着做\(mid+1\)分段存在至少一個公共划分點,且滿足用前者划分的一段前綴拼上后者划分的一段后綴恰好可以得到原問題的一組合法解。
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
const int N=1e5+5;
int n,k,a[N],p1[N],t1,p2[N],t2;
int check(ll mid){
ll sum=0;int res=0;
for(int i=1;i<=n;++i){
sum+=a[i];
if(sum>=mid)++res,sum=0;
}
return res;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
ll l=0,r=5e9,res;
while(l<=r){
ll mid=l+r>>1;
if(check(mid)>=k)res=mid,l=mid+1;
else r=mid-1;
}
ll sum=0;
for(int i=1;i<=n;++i){
sum+=a[i];
if(sum>=res)p1[++t1]=i,sum=0;
}
sum=0;
for(int i=n;i;--i){
sum+=a[i];
if(sum>=res+1)p2[++t2]=i,sum=0;
}
puts("Yes");
if(t1==k&&p1[k]==n){
for(int i=1;i<k;++i)printf("%d ",p1[i]);
puts("");return 0;
}
for(int i=1;i<=t1;++i)
if(k-i>=1&&k-i<=t2&&p1[i]+1==p2[k-i]){
for(int j=1;j<=i;++j)printf("%d ",p1[j]);
for(int j=k-i-1;j;--j)printf("%d ",p2[j]-1);
puts("");return 0;
}
return 0;
}
K. Knowledge
description
有一個僅包含兩種字符ab
的字符串\(S\),可對其進行的操作為在任意位置插入或刪除子串aa
,bbb
以及ababab
。問字符串\(S\)在進行若干次操作后能夠得到多少種不同的長度為\(m\)的串。
\(1 \le |S| \le 3\times 10^5, 1 \le m \le 10^9.\)
solution
實際上任意一個串都可以經過若干次操作得到一個唯一的長度最短的串,而這個串的最長長度不會超過\(4\)。
通過大力枚舉可以得知這個“唯一的長度最短的串”只有\(12\)種,因此矩乘計算答案即可,復雜度為\(O(12^3\log m)\)。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
const int mod=998244353;
map<string,string>trans;
map<string,int>state;
string str[15];int tot,n,m;
void add(string s){str[state[s]=++tot]=s;}
int f(string s){
string t;
for(int i=0;i<s.size();++i){
t+=s[i];
if(trans.count(t))t=trans[t];
}
return state[t];
}
struct mat{
int a[15][15];
mat(){memset(a,0,sizeof(a));}
int *operator [](int x){return a[x];}
mat operator * (mat b){
mat c;
for(int i=1;i<=tot;++i)
for(int j=1;j<=tot;++j)
for(int k=1;k<=tot;++k)
c[i][k]=(c[i][k]+1ll*a[i][j]*b[j][k])%mod;
return c;
}
}S,T;
int main(){
add("");
add("a");
add("b");
trans["aa"]="";
add("ab");
add("ba");
add("bb");
add("aba");
add("abb");
trans["baa"]="b";
add("bab");
add("bba");
trans["bbb"]="";
trans["abaa"]="ab";
trans["abab"]="bba";
trans["abba"]="bab";
trans["abbb"]="a";
trans["baba"]="abb";
add("babb");
trans["bbaa"]="bb";
add("bbab");
trans["babba"]="bbab";
trans["babbb"]="ba";
trans["bbaba"]="babb";
trans["bbabb"]="aba";
for(int i=1;i<=tot;++i){
S[i][i]=1;
++T[i][f(str[i]+"a")];
++T[i][f(str[i]+"b")];
}
cin>>n>>str[0]>>m;
while(m){if(m&1)S=S*T;T=T*T;m>>=1;}
cout<<S[f("")][f(str[0])]<<endl;
return 0;
}