月初打的比赛,今天才把坑填完了。。
A.
要不出现
t
r
y
g
u
b
trygub
trygub的子序列,只需要给
s
s
s排序即符合条件了。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
const int maxn=1007;
using namespace std;
int T,n;
char s[maxn];
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
scanf("%s",s+1);
sort(s+1,s+n+1);
for (int i=1;i<=n;i++) printf("%c",s[i]);
printf("\n");
}
}
B.
显然只有当一个点可以与其他点的距离小于等于
k
k
k时,才能使得最终所有的点位于同一个点。所以答案不是
1
1
1就是
−
1
-1
−1。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
const int maxn=107;
using namespace std;
int T,n,k;
int a[maxn],b[maxn];
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
int ans=-1;
for (int i=1;i<=n;i++)
{
int flag=1;
for (int j=1;j<=n;j++)
{
if (abs(a[i]-a[j])+abs(b[i]-b[j])>k)
{
flag=0;
break;
}
}
if (flag)
{
ans=1;
break;
}
}
printf("%d\n",ans);
}
}
C1.
我们可以对棋盘进行染色,具体来说就是使
(
i
+
j
)
%
3
(i+j)\%3
(i+j)%3的值相同的点
(
i
,
j
)
(i,j)
(i,j)染上相同的颜色。我们把其中一种颜色上的
X
X
X变成
O
O
O,那么一定是一种合法的方案。假设棋盘上有
k
k
k个
X
X
X,那么我们一定可以选择某一种颜色,这种颜色上的
X
X
X的个数小于等于
k
/
3
k/3
k/3。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
const int maxn=307;
using namespace std;
int T,n;
char s[maxn][maxn];
int num[2][3];
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
memset(num,0,sizeof(num));
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
if (s[i][j]=='X') num[0][(i+j)%3]++;
if (s[i][j]=='O') num[1][(i+j)%3]++;
}
}
int x=0,y=0,minn=1e9;
for (int i=0;i<3;i++)
{
for (int j=0;j<3;j++)
{
if (i==j) continue;
if (num[0][i]+num[1][j]<minn)
{
minn=num[0][i]+num[1][j];
x=i,y=j;
}
}
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
if (s[i][j]=='.')
{
printf("%c",'.');
continue;
}
if ((i+j)%3==x) printf("%c",'O');
else
{
if ((i+j)%3==y) printf("%c",'X');
else printf("%c",s[i][j]);
}
}
printf("\n");
}
}
}
C2.
我们同样按上述方法进行染色。我们考虑选择其中两种颜色,一种全部变成
X
X
X,另一种全部变成
O
O
O。可以知道一共有
6
6
6种选择方式。
设
f
[
1..3
]
[
O
/
X
]
f[1..3][O/X]
f[1..3][O/X]表示把第
i
i
i种颜色的区域全部变成
X
X
X或者
O
O
O需要改变的格子的个数。
可以知道这
6
6
6个
f
f
f值的和就是
k
k
k,每种选择需要改变的格子数量为
f
[
i
]
[
O
]
+
f
[
j
]
[
X
]
(
i
≠
j
)
f[i][O]+f[j][X](i≠j)
f[i][O]+f[j][X](i=j)
上述的
6
6
6种选择方式的和就是
2
∗
k
2*k
2∗k,那么改变格子数量的那种方式一定小于
1
6
∗
(
2
k
)
=
k
/
3
\frac{1}{6}*(2k)=k/3
61∗(2k)=k/3。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
const int maxn=307;
using namespace std;
int T,n;
char s[maxn][maxn];
int num[2][3];
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
memset(num,0,sizeof(num));
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
if (s[i][j]=='X') num[0][(i+j)%3]++;
if (s[i][j]=='O') num[1][(i+j)%3]++;
}
}
int x=0,y=0,minn=1e9;
for (int i=0;i<3;i++)
{
for (int j=0;j<3;j++)
{
if (i==j) continue;
if (num[0][i]+num[1][j]<minn)
{
minn=num[0][i]+num[1][j];
x=i,y=j;
}
}
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
if (s[i][j]=='.')
{
printf("%c",'.');
continue;
}
if ((i+j)%3==x) printf("%c",'O');
else
{
if ((i+j)%3==y) printf("%c",'X');
else printf("%c",s[i][j]);
}
}
printf("\n");
}
}
}
D.
考虑由原序列产生的每个长度的序列。
长度为
n
n
n的序列(原序列)可以先直接判断。
考虑长度为
[
1
,
n
−
1
]
[1,n-1]
[1,n−1]的序列。
长度为 1 1 1的序列合法当且仅当序列最小值为 1 1 1。
长度为 2 2 2的序列合法,那么只存在一个 1 1 1,并且在最左端或者最右端,去掉 1 1 1后剩下的序列最小值为 2 2 2。
长度为 3 3 3的序列合法,那么首先长度为 2 2 2的序列必须合法。去掉 1 1 1后,只存在一个 2 2 2,并且位于最左端或者最右端,去掉 1 1 1和 2 2 2后的序列最小值为 3 3 3。
根据上述推导,长度为
k
k
k的序列合法当且仅当长度为
k
−
1
k-1
k−1的序列合法,去掉
[
1
,
k
−
2
]
[1,k-2]
[1,k−2]的数后,只存在一个
k
−
1
k-1
k−1,并且位于最左端或者最右端,且剩下部分最小值为
k
k
k。
具体可以参考代码。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
const int maxn=3e5+7;
using namespace std;
int T,n;
int f[maxn][20],lg[maxn],vis[maxn],ans[maxn];
int getmin(int x,int y)
{
int len=y-x+1;
int k=lg[len];
return min(f[x][k],f[y-(1<<k)+1][k]);
}
int main()
{
for (int i=1;i<=3e5;i++) lg[i]=trunc(log(i+0.5)/log(2));
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&f[i][0]);
vis[i]=0;
}
int flag=1;
for (int i=1;i<=n;i++)
{
if (vis[f[i][0]])
{
flag=0;
break;
}
else vis[f[i][0]]=1;
}
printf("%d",flag);
for (int j=1;j<=lg[n];j++)
{
for (int i=1;i<=n-(1<<(j-1))+1;i++) f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
int l=1,r=n;
for (int i=1;i<n;i++)
{
if (getmin(l,r)==i) ans[n-i+1]=1;
else ans[n-i+1]=0;
if (f[l][0]==i)
{
if (f[r][0]!=i) l++;
else
{
for (int j=i+1;j<n;j++) ans[n-j+1]=0;
break;
}
}
else
{
if (f[r][0]==i) r--;
else
{
for (int j=i+1;j<n;j++) ans[n-j+1]=0;
break;
}
}
}
for (int i=2;i<=n;i++) printf("%d",ans[i]);
printf("\n");
}
}
E.
我们可以先考虑差分约束。
最短路一定满足
d
x
+
w
≥
d
y
d_x+w≥d_y
dx+w≥dy,也就意味着存在约束
d
y
−
d
x
≤
w
d_y-d_x≤w
dy−dx≤w,我们可以从
x
x
x向
y
y
y连一条
w
w
w的边。
因此,由限制的边可以看做
1
≤
a
y
−
a
x
≤
1
1≤a_y-a_x≤1
1≤ay−ax≤1,无限制的边可以看做
−
1
≤
a
y
−
a
x
≤
1
-1≤a_y-a_x≤1
−1≤ay−ax≤1。
但题意后面这个还有一个限制,就是
a
y
−
a
x
a_y-a_x
ay−ax不能为0。因此我们可以对图进行黑白染色,可以知道,黑色的点与白色的点
a
a
a值的奇偶性不同。如果原图能进行黑白染色,而且差分约束没有负环,那么就可以有解。
我们可以用Floyd跑最短路,
a
a
a的最大差值就是
m
a
x
(
d
i
s
(
i
,
j
)
)
max(dis(i,j))
max(dis(i,j))。然后可以根据差分约束求出一组解。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
const int maxn=207;
const int inf=0x3f3f3f3f;
using namespace std;
int n,m,x,y,op;
int a[maxn][maxn],f[maxn][maxn];
int v[maxn];
queue <int> q;
bool bfs()
{
v[1]=1;
q.push(1);
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=1;i<=n;i++)
{
if (a[x][i])
{
if (v[i])
{
if (v[x]==v[i]) return 0;
}
else
{
q.push(i);
v[i]=-v[x];
}
}
}
}
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++) f[i][j]=inf;
f[i][i]=0;
}
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&op);
a[x][y]=a[y][x]=1;
f[x][y]=1;
if (op) f[y][x]=-1;
else f[y][x]=1;
}
if (!bfs())
{
printf("NO\n");
return 0;
}
for (int k=1;k<=n;k++)
{
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
}
}
int flag=1;
for (int i=1;i<=n;i++)
{
if (f[i][i]<0)
{
flag=0;
break;
}
}
if (!flag)
{
printf("NO\n");
return 0;
}
int ans=0;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
if (f[i][j]>ans)
{
ans=f[i][j];
x=i;
y=j;
}
}
}
printf("YES\n");
printf("%d\n",ans);
for (int i=1;i<=n;i++) printf("%d ",f[x][i]);
}
F.
首先如果有一个数超过
(
n
+
1
)
/
2
(n+1)/2
(n+1)/2,那么一定不合法。
然后我们显然可以知道,每一个
a
i
=
a
i
−
1
a_i=a_{i-1}
ai=ai−1的位置都需要改变,假设我们有
k
k
k个这样的位置。我们考虑把序列从这些位置分开,可以产生
k
+
1
k+1
k+1个段,我们记录每个段最左边与最右边的数,假设出现最多的数为
x
x
x,出现了
y
y
y次。
如果
y
≤
k
+
2
y≤k+2
y≤k+2,我们可以通过对这些段进行翻转、按任意顺序排列,使得前一个段最右边的数与后一个段最左边的数不同,此时的答案就是
k
k
k。
而
y
>
k
+
2
y>k+2
y>k+2个,那么就需要把一些段拆开。如果我们选择拆分的位置左右的数有一个与
x
x
x相同,那么这种拆分会使
y
y
y与
k
k
k都增加
1
1
1,是无效的。而当左右的数都与
x
x
x不同,此时
k
k
k增加
1
1
1,
y
y
y不变。因此我们每次拆分可以使得
y
y
y与
k
+
2
k+2
k+2的差减小
1
1
1。因此答案就是
y
−
2
y-2
y−2。
而每次选择左右的数不同的位置一定是可以的,我们可以把原序列直接拆成
n
n
n段,再把无效的位置连回去就可以了。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
const int maxn=1e5+7;
using namespace std;
int n,T;
int a[maxn],b[maxn],f[maxn];
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
for (int i=1;i<=n;i++) b[i]=f[i]=0;
int k=0;
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[a[i]]++;
if (a[i]==a[i-1]) f[a[i]]+=2,k++;
}
f[a[1]]++,f[a[n]]++;
int x=0,y=0;
for (int i=1;i<=n;i++)
{
x=max(x,b[i]);
y=max(y,f[i]);
}
if (x>(n+1)/2) printf("-1\n");
else if (y>k+2) printf("%d\n",y-2);
else printf("%d\n",k);
}
}
G.
设
n
n
n为字符集大小。
我们可以知道,字母之间的转化可以看做是一棵树。
考虑
f
[
s
t
a
]
f[sta]
f[sta]表示所选字母状态为
s
t
a
sta
sta,是否可以把这些字母都变为同一个字母,且这个字母可以继续转化。
设
l
[
s
t
a
]
l[sta]
l[sta]表示最左边的在状态
s
t
a
sta
sta中的字母的下标。
设
r
[
s
t
a
]
r[sta]
r[sta]表示最右边的在状态
s
t
a
sta
sta中的字母的下标。
设
c
n
t
[
s
t
a
]
cnt[sta]
cnt[sta]表示在状态
s
t
a
sta
sta中的字母的个数。
我们设
g
[
s
t
a
]
g[sta]
g[sta]表示所选字母状态为
s
t
a
sta
sta,且都变成同一个字母,是否可以继续转化。
那么
g
[
s
t
a
]
=
(
k
∗
(
r
[
s
t
a
]
−
l
[
s
t
a
]
)
<
=
c
n
t
[
s
t
a
]
)
g[sta]=(k*(r[sta]-l[sta])<=cnt[sta])
g[sta]=(k∗(r[sta]−l[sta])<=cnt[sta])
考虑两种转移,第一种考虑
s
t
a
sta
sta中的一个字母对应的状态位置
x
x
x,
f
[
s
t
a
]
=
f
[
s
t
a
−
2
x
]
&
g
[
s
t
a
]
f[sta]=f[sta-2^{x}]\&g[sta]
f[sta]=f[sta−2x]&g[sta]
因为
f
[
s
t
a
−
2
x
]
f[sta-2^x]
f[sta−2x]可以转化,直接把他转化成
x
x
x即可,同时要保证
f
[
s
t
a
]
f[sta]
f[sta]可以转化,所以要
&
g
[
s
t
a
]
\& g[sta]
&g[sta],相当于儿子向父亲的转移。
第二种考虑枚举一个子集,
f
[
s
t
a
]
=
f
[
s
u
b
]
&
f
[
s
t
a
x
o
r
s
u
b
]
f[sta]=f[sub]\&f[sta\ xor\ sub]
f[sta]=f[sub]&f[sta xor sub]
因为
f
[
s
u
b
]
f[sub]
f[sub]与
f
[
s
t
a
x
o
r
s
u
b
]
f[sta\ xor\ sub]
f[sta xor sub]都可以转化,那么他们的并一定是可以转化的(只需要把他们转化成同一个字母即可),相当于合并兄弟的子树。
如果
f
[
s
u
b
−
2
x
]
f[sub-2^x]
f[sub−2x]为
t
r
u
e
true
true就说明最终可以转化成第
x
x
x位对应的字母了。
但这样复杂度是
O
(
n
∗
3
n
)
O(n*3^n)
O(n∗3n)的,不能通过。我们考虑枚举子集的转移,我们发现,如果两个集合
a
a
a,
b
b
b的区间
(
l
[
a
]
,
r
[
a
]
)
(l[a],r[a])
(l[a],r[a]),
(
l
[
b
]
,
r
[
b
]
)
(l[b],r[b])
(l[b],r[b])有交,且
f
[
a
]
f[a]
f[a]与
f
[
b
]
f[b]
f[b]均为真时,假设集合
a
a
a最终变成字母
p
p
p,集合
b
b
b最终变成字母
q
q
q。因为都可以继续转移,
p
p
p,
q
q
q都变成一个新的字母
r
r
r,可以通过
p
p
p变成
q
q
q,
q
q
q变成
r
r
r来实现。
如果我们把字符从左到右编号,那么没有交的字符集合一定是 1 1 1到 i i i个字符为集合 a a a, i i i到 n n n个字符为集合 b b b。因此没有交的子集最多有 n n n个。复杂度 O ( n 2 ∗ 2 n ) O(n^2*2^n) O(n2∗2n)。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <map>
#define LL long long
const int S=1050000;
const int N=21;
using namespace std;
int n,x,y,tot;
int bit[N],a[N],b[N],num[N];
int l[S],r[S],cnt[S];
bool f[S],g[S];
char ch,d[N],ans[N];
map <int,int> lg;
int main()
{
scanf("%d%d%d\n",&n,&x,&y);
for (int i=1;i<=n;i++)
{
scanf("%c",&ch);
int flag=1;
for (int j=1;j<=tot;j++)
{
if (ch==d[j])
{
num[j]++;
b[j]=i;
flag=0;
break;
}
}
if (flag)
{
++tot;
a[tot]=i;
b[tot]=i;
num[tot]=1;
d[tot]=ch;
}
}
bit[0]=1;
lg[1]=0;
for (int i=1;i<=tot;i++) bit[i]=bit[i-1]*2,lg[bit[i]]=i;
for (int i=1;i<bit[tot];i++)
{
int lowbit=i&(-i);
int p=lg[lowbit]+1;
if (lowbit==i)
{
l[i]=a[p];
r[i]=b[p];
cnt[i]=num[p];
}
else
{
l[i]=min(l[i-lowbit],a[p]);
r[i]=max(r[i-lowbit],b[p]);
cnt[i]=cnt[i-lowbit]+num[p];
}
if ((LL)(r[i]-l[i]+1)*(LL)x<=(LL)cnt[i]*(LL)y) g[i]=true;
else g[i]=false;
}
f[0]=g[0]=true;
for (int i=1;i<bit[tot];i++)
{
int sta=0;
for (int j=1;j<=tot;j++)
{
if (i&bit[j-1])
{
sta|=bit[j-1];
if (!f[i]) f[i]=f[sta]&f[i^sta];
if ((g[i]) && (!f[i])) f[i]=f[i-bit[j-1]];
}
}
}
if (!f[bit[tot]-1])
{
printf("0");
return 0;
}
int nd=0;
for (int i=1;i<=tot;i++)
{
if (f[bit[tot]-1-bit[i-1]]) ans[++nd]=d[i];
}
printf("%d",nd);
sort(ans+1,ans+nd+1);
for (int i=1;i<=nd;i++) printf(" %c",ans[i]);
}
H1.
对于一个确定的序列,设
B
o
d
d
B_{odd}
Bodd和
B
e
v
e
n
B_{even}
Beven为奇数位黑色的个数与偶数位黑色的个数,答案就是
1
2
∣
B
o
d
d
−
B
e
v
e
n
∣
\frac{1}{2}|B_{odd}-B_{even}|
21∣Bodd−Beven∣。
然后我们可以考虑确定的
∣
B
o
d
d
−
B
e
v
e
n
∣
|B_{odd}-B_{even}|
∣Bodd−Beven∣,假设不确定的位置有q个,通过推导可以发现答案就是
1
2
q
∑
i
=
0
q
∣
n
2
−
W
o
d
d
−
B
e
v
e
n
−
i
∣
(
q
i
)
\frac{1}{2^q}\sum_{i=0}^{q}|\frac{n}{2}-W_{odd}-B_{even}-i|\binom{q}{i}
2q1∑i=0q∣2n−Wodd−Beven−i∣(iq)。其中,
∣
n
2
−
W
o
d
d
−
B
e
v
e
n
−
i
∣
|\frac{n}{2}-W_{odd}-B_{even}-i|
∣2n−Wodd−Beven−i∣需要是偶数。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const int maxn=2e5+7;
const LL mod=998244353;
using namespace std;
int n,m,p,q;
char s[maxn];
LL jc[maxn],inv[maxn];
LL ksm(LL x,LL y)
{
if (y==0) return 1;
LL c=ksm(x,y/2);
c=(c*c)%mod;
if (y&1) c=(c*x)%mod;
return c;
}
LL C(int n,int m)
{
return jc[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
scanf("%d%d",&n,&m);
scanf("%s",s+1);
p=n/2;
for (int i=1;i<=n;i++)
{
if ((i%2==1) && (s[i]=='w')) p--;
if ((i%2==0) && (s[i]=='b')) p--;
if (s[i]=='?') q++;
}
jc[0]=1;
for (int i=1;i<=q;i++) jc[i]=jc[i-1]*(LL)i%mod;
inv[q]=ksm(jc[q],mod-2);
for (int i=q;i>0;i--) inv[i-1]=inv[i]*(LL)i%mod;
LL ans=0;
for (int i=0;i<=q;i++)
{
LL k=abs(p-i);
if (k%2==0) ans=(ans+C(q,i)*k%mod)%mod;
}
ans=ans*ksm(ksm(2,mod-2),q)%mod;
printf("%lld\n",ans);
}
H2.考虑把绝对值拆开,根据
i
∗
(
n
i
)
=
n
∗
(
n
−
1
i
−
1
)
i*\binom{n}{i}=n*\binom{n-1}{i-1}
i∗(in)=n∗(i−1n−1)与
(
n
i
)
=
(
n
−
1
i
−
1
)
+
(
n
−
1
i
)
\binom{n}{i}=\binom{n-1}{i-1}+\binom{n-1}{i}
(in)=(i−1n−1)+(in−1)。
如果
n
2
−
W
o
d
d
−
B
e
v
e
n
\frac{n}{2}-W_{odd}-B_{even}
2n−Wodd−Beven与
q
q
q增加或者减小
2
2
2,答案是可以相互推的。
所以需要维护两个值分别为奇偶时的答案
4
4
4个值即可。
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll MOD = 998244353;
const int mxN = 200001;
ll fact[mxN], ifact[mxN];
ll nCr(int n, int r) {
if(r > n || r < 0)return 0;
return ((fact[n]*ifact[n-r])%MOD*ifact[r])%MOD;
}
ll binpow(ll a,ll b) {
ll res=1;
while(b) {
if(b&1)res=(res*a)%MOD;
a=(a*a)%MOD;
b>>=1;
}
return res;
}
ll modInv(ll a) {
return binpow(a, MOD-2);
}
string s;
int n, q;
int b[2], w[2], f[2];
ll tpow[mxN];
ll tinv[mxN];
ll ans[4];
ll lft, func;
void print() {
ll res = (ans[1]*lft + ans[2]*func)%MOD;
res = (ans[0]*func + ans[3]*lft - res + MOD)%MOD;
res = (res * tinv[lft])%MOD;
cout<<res<<"\n";
}
void fix() {
ans[0] = ans[1] = ans[2] = ans[3] = 0;
int parity = abs(func)&1;
for(int i = parity; i <= min(func-2, lft); i+=2) {
ans[0] = (ans[0] + nCr(lft, i))%MOD;
}
for(int i = parity; i <= min(func-2, lft); i+=2) {
ans[1] = (ans[1] + nCr(lft-1, i-1))%MOD;
}
for(int i = func+2; i <= lft; i+=2) {
ans[3] = (ans[3] + nCr(lft-1, i-1))%MOD;
}
for(int i = func+2; i <= lft; i+=2) {
ans[2] = (ans[2] + nCr(lft, i))%MOD;
}
}
void inc() { // increase the numerator
ans[0] = (2*ans[0] - nCr(lft-1, func-2) + MOD)%MOD;
ans[1] = (2*ans[1] - nCr(lft-2, func-3) + MOD)%MOD;
ans[2] = tpow[lft-1] - ans[2];
ans[2] = 2*ans[2] - nCr(lft-1, func);
ans[2] = ((tpow[lft] - ans[2])%MOD + MOD)%MOD;
ans[3] = tpow[lft-2] - ans[3];
ans[3] = 2*ans[3] - nCr(lft-2, func-1);
ans[3] = ((tpow[lft-1] - ans[3])%MOD + MOD)%MOD;
lft++;
}
void binc() { //increase denominator
ans[0] = (ans[0] + nCr(lft-1, func-1))%MOD;
ans[1] = (ans[1] + nCr(lft-2, func-2))%MOD;
ans[2] = (ans[2] - nCr(lft-1, func+1) + MOD)%MOD;
ans[3] = (ans[3] - nCr(lft-2, func) + MOD)%MOD;
func++;
}
void dec() { //decrease the numerator
ans[0] = ((ans[0] + nCr(lft-2, func-2))*tinv[1])%MOD;
ans[1] = ((ans[1] + nCr(lft-3, func-3))*tinv[1])%MOD;
ans[2] = tpow[lft-1] - ans[2];
ans[2] = ((ans[2] + nCr(lft-2, func))*tinv[1])%MOD;
ans[2] = (tpow[lft-2] - ans[2] + MOD)%MOD;
ans[3] = tpow[lft-2] - ans[3];
ans[3] = ((ans[3] + nCr(lft-3, func-1))*tinv[1])%MOD;
ans[3] = (tpow[lft-3] - ans[3] + MOD)%MOD;
lft--;
}
void bdec() { //decrease denominator
ans[0] = (ans[0] - nCr(lft-1, func-2) + MOD)%MOD;
ans[1] = (ans[1] - nCr(lft-2, func-3) + MOD)%MOD;
ans[2] = (ans[2] + nCr(lft-1, func))%MOD;
ans[3] = (ans[3] + nCr(lft-2, func-1))%MOD;
func--;
}
int main() {
fact[0] = 1;
tpow[0] = 1;
for(int i = 1; i < mxN; ++i){
fact[i] = (fact[i-1]*i)%MOD;
tpow[i] = (tpow[i-1]*2)%MOD;
}
ifact[mxN-1] = modInv(fact[mxN-1]);
tinv[mxN-1] = modInv(tpow[mxN-1]);
for(int i = mxN-2; i >= 0; --i) {
ifact[i] = (ifact[i+1]*(i+1))%MOD;
tinv[i] = (tinv[i+1]*2)%MOD;
}
cin>>n>>q>>s;
for(int i = 0; i < n; ++i) {
if(s[i]=='w') {
w[i&1]++;
}else if(s[i]=='b') {
b[i&1]++;
}else {
f[i&1]++;
}
}
lft = f[0] + f[1];
func = n/2 - b[1] - w[0];
fix();
print();
for(int i = 0; i < q; ++i) {
int a;
char c;
cin>>a>>c;
a--;
//uncolor
if(s[a]=='w') {
inc();
if((a&1)^1) binc();
}else if(s[a]=='b') {
inc();
if(a&1) binc();
}
//recolor
if(c=='w') {
dec();
if((a&1)^1) bdec();
}else if(c=='b') {
dec();
if(a&1) bdec();
}
s[a] = c;
if(lft<=2) fix();
print();
}
return 0;
}