Codeforces Global Round 12 CF1450A-H 题解

月初打的比赛,今天才把坑填完了。。

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 2k,那么改变格子数量的那种方式一定小于 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,n1]的序列。

长度为 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 k1的序列合法,去掉 [ 1 , k − 2 ] [1,k-2] [1,k2]的数后,只存在一个 k − 1 k-1 k1,并且位于最左端或者最右端,且剩下部分最小值为 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+wdy,也就意味着存在约束 d y − d x ≤ w d_y-d_x≤w dydxw,我们可以从 x x x y y y连一条 w w w的边。
因此,由限制的边可以看做 1 ≤ a y − a x ≤ 1 1≤a_y-a_x≤1 1ayax1,无限制的边可以看做 − 1 ≤ a y − a x ≤ 1 -1≤a_y-a_x≤1 1ayax1
但题意后面这个还有一个限制,就是 a y − a x a_y-a_x ayax不能为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=ai1的位置都需要改变,假设我们有 k k k个这样的位置。我们考虑把序列从这些位置分开,可以产生 k + 1 k+1 k+1个段,我们记录每个段最左边与最右边的数,假设出现最多的数为 x x x,出现了 y y y次。
如果 y ≤ k + 2 y≤k+2 yk+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 y2
而每次选择左右的数不同的位置一定是可以的,我们可以把原序列直接拆成 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[sta2x]&g[sta]
因为 f [ s t a − 2 x ] f[sta-2^x] f[sta2x]可以转化,直接把他转化成 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[sub2x] t r u e true true就说明最终可以转化成第 x x x位对应的字母了。
但这样复杂度是 O ( n ∗ 3 n ) O(n*3^n) O(n3n)的,不能通过。我们考虑枚举子集的转移,我们发现,如果两个集合 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(n22n)

代码:

#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}| 21BoddBeven
然后我们可以考虑确定的 ∣ B o d d − B e v e n ∣ |B_{odd}-B_{even}| BoddBeven,假设不确定的位置有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} 2q1i=0q2nWoddBeveni(iq)。其中, ∣ n 2 − W o d d − B e v e n − i ∣ |\frac{n}{2}-W_{odd}-B_{even}-i| 2nWoddBeveni需要是偶数。

代码:

#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(i1n1) ( n i ) = ( n − 1 i − 1 ) + ( n − 1 i ) \binom{n}{i}=\binom{n-1}{i-1}+\binom{n-1}{i} (in)=(i1n1)+(in1)
如果 n 2 − W o d d − B e v e n \frac{n}{2}-W_{odd}-B_{even} 2nWoddBeven 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值