AtCoder Regular Contest 151

Preface

这周六食物中毒,上吐下泻还发烧,被拉到校医院隔离了一天人麻了

然后周日晚上又去做模拟四级考试,没赶上比赛,只能后面来补题了

这场又是好难的一场,ABD磕磕绊绊地写完了,C有点思路但是一些情况没想清楚


A - Equal Hamming Distances

首先我们考虑若两个串在某个位置相等,那么显然放什么数都没影响,随性就给个0

考虑不同的位置,显然我们可以先贪心地从前往后确定,先考虑放0,然后验证后面的数是否存在某种情况符合条件

设此时两个串的不匹配数分别为\(t_1,t_2\),后面还有\(x\)个位置不相等,则合法的条件为\(x\ge |t_1-t_2|\and 2|(x-|t_1-t_2|)\)

#include<cstdio>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int n,sum,t1,t2; char s[N],t[N],ans[N];
inline bool check(CI left)
{
	int det=abs(t1-t2); return left>=det&&!((left-det)&1);
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d%s%s",&n,s+1,t+1),i=1;i<=n;++i) if (s[i]!=t[i]) ++sum;
	bool flag=1; for (i=1;i<=n&&flag;++i)
	if (s[i]==t[i]) ans[i]='0'; else
	{
		t1+=s[i]!='0'; t2+=t[i]!='0';
		if (check(sum-1)) { ans[i]='0'; --sum; continue; }
		t1-=s[i]!='0'; t2-=t[i]!='0';
		t1+=s[i]!='1'; t2+=t[i]!='1';
		if (check(sum-1)) { ans[i]='1'; --sum; continue; }
		t1-=s[i]!='1'; t2-=t[i]!='1';
		flag=0;
	}
	if (flag) for (i=1;i<=n;++i) putchar(ans[i]); else puts("-1");
	return 0; 
}

B - A < AP

这种问题我们可以按序考虑第一个小于的位置,那么前面的全是等于,后面的就随便放了

\(coef_i\)为做到第\(i\)个操作时前面的数都是对应相等的关系的方案数

我们定义一个数为未使用当且仅当之前的相等关系不包括这个数,\(k\)表示此时未操作的数的个数

首先考虑统计答案,先是分类讨论下:

  • \(i,p_i\)均未使用,则\(ans+=coef_i\times C_m^2\times m^k\)
  • \(i,p_i\)中有一个未使用,则\(ans+=\frac{coef_i\times C_m^2}{m}\times m^k\)
  • \(i,p_i\)均使用,则\(ans+=\frac{coef_i\times C_m^2}{m^2}\times m^k\)

因为每当一个数被使用时它的取值将固定,则对应的方案数要除以\(m\)

那么现在考虑\(coef_i\)的转移,其实和上面的类似:

  • \(i,p_i\)均未使用,则\(coef_i=coef_{i-1}\times m\)
  • \(i,p_i\)中有一个未使用,则\(coef_i=coef_{i-1}\)
  • \(i,p_i\)均使用,则视情况讨论:
    • \(i,p_i\)之前是相等的,则\(coef_i=coef_{i-1}\)
    • \(i,p_i\)之前是不相等,则\(coef_i=\frac{coef_{i-1}}{m}\)

不难发现我们可以直接把所有的相等关系用并查集维护,这样就可以直接做了

(其实写的时候我才发现好像总的系数就是\(m^t\)\(t\)就是连通块个数,但是懒得改了)

#include<cstdio>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,mod=998244353;
int n,m,invm,C2m,p[N],ans,coef,vis[N],pw[N],cur;
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void inc(int &x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
namespace DSU
{
	int fa[N];
	inline void init(CI n)
	{
		for (RI i=1;i<=n;++i) fa[i]=i;
	}
	inline int getfa(CI x)
	{
		return fa[x]!=x?fa[x]=getfa(fa[x]):x;
	}
	inline bool query(CI x,CI y)
	{
		return getfa(x)==getfa(y);
	}
	inline void link(CI x,CI y)
	{
		fa[getfa(x)]=getfa(y);
	}
};
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d%d",&n,&m),cur=n,pw[0]=i=1;i<=n;++i) scanf("%d",&p[i]),pw[i]=1LL*pw[i-1]*m%mod;
	for (DSU::init(n),invm=quick_pow(m),C2m=1LL*m*(m-1)/2LL%mod,coef=i=1;i<=n;++i)
	if (i!=p[i])
	{
		if (!vis[i]&&!vis[p[i]]) inc(ans,1LL*coef*C2m%mod*pw[cur-2]%mod),cur-=2,DSU::link(i,p[i]),coef=1LL*coef*m%mod;
		else if ((vis[i]+vis[p[i]])==1) inc(ans,1LL*coef*invm%mod*C2m%mod*pw[cur-1]%mod),--cur,DSU::link(i,p[i]);
		else if (!DSU::query(i,p[i])) inc(ans,1LL*coef*invm%mod*invm%mod*C2m%mod*pw[cur]%mod),DSU::link(i,p[i]),coef=1LL*coef*invm%mod;
		vis[i]=vis[p[i]]=1;
	} else
	{
		if (!vis[i]) coef=1LL*coef*m%mod,--cur,vis[i]=1;
	}
	return printf("%d",ans),0;
}

C - 01 Game

好神的博弈论!

首先这种公平博弈考虑SG函数,显然整个棋盘被分成了\(M+1\)块,我们只要求出每一块的SG函数即可

首先考虑\(M=0\)的情况,我们考虑使用以下策略:

  • \(n\)为奇数时,先手在棋盘中间任下一颗棋子,接下来无论后手再哪里落子,先手总可以在该位置关于中心对称处下一颗相同颜色的棋子,因此该情况先手必胜,即\(SG=1\)
  • \(n\)为偶数时,不论先手怎么落子,后手总可以在该位置关于中心线对称处下一颗不同颜色的棋子,因此该情况后手必胜,即\(SG=0\)

接下来我们先考虑那些两端都有棋子的区间段,也是先分类讨论:

  • 当两端的棋子颜色相同时:不难发现当长度为奇数时先手一定可以在中间随便下一颗棋然后转化成上面的情形,先手必胜,\(SG=1\)
  • 当两端的棋子颜色不同时:不难发现当长度为偶数时后手一定可以用上面的方法进行镜像落相反的棋子的方法来取胜,因此\(SG=0\)

接下来我们以这些情况为基础,继续推导:

  • 当两端的棋子颜色相同且长度为偶数时,先手可以在距某个端点空一格的位置填一个与端点颜色不同的棋子。不难发现此时这个空格不能填任何棋子,并且剩下的部分变成了两端的棋子颜色不同且长度为偶数的情况,即必败态。因此这个情况时必胜态,\(SG=1\)
  • 当两端的棋子颜色不同且长度为奇数时,不论先手在哪里落子,一定会出现一段两端的棋子颜色相同和情形和一段两端的棋子颜色不同的情况(也可能不存在)。由于两端的棋子颜色相同时是必胜态,而另一边还是两端的棋子颜色不同的局面。因此该状态的每一个后继都是必胜态,其为必败态,\(SG=0\)

因此我们现在只要考虑一段有棋子一段是空隙的情况即可,不难发现一定会产生某种两端都有棋子的情况和一段只有一端有棋子的情况

因此对于一个长度为\(x\)的段,可以转移到\(0,1,2,\cdots,x-1\)的情况,因此其\(SG=x\)

知道了这点就可以直接\(O(M)\)计算了

#include<cstdio>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,mod=998244353;
int n,m,invm,C2m,p[N],ans,coef,vis[N],pw[N],cur;
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void inc(int &x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
namespace DSU
{
	int fa[N];
	inline void init(CI n)
	{
		for (RI i=1;i<=n;++i) fa[i]=i;
	}
	inline int getfa(CI x)
	{
		return fa[x]!=x?fa[x]=getfa(fa[x]):x;
	}
	inline bool query(CI x,CI y)
	{
		return getfa(x)==getfa(y);
	}
	inline void link(CI x,CI y)
	{
		fa[getfa(x)]=getfa(y);
	}
};
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d%d",&n,&m),cur=n,pw[0]=i=1;i<=n;++i) scanf("%d",&p[i]),pw[i]=1LL*pw[i-1]*m%mod;
	for (DSU::init(n),invm=quick_pow(m),C2m=1LL*m*(m-1)/2LL%mod,coef=i=1;i<=n;++i)
	if (i!=p[i])
	{
		if (!vis[i]&&!vis[p[i]]) inc(ans,1LL*coef*C2m%mod*pw[cur-2]%mod),cur-=2,DSU::link(i,p[i]),coef=1LL*coef*m%mod;
		else if ((vis[i]+vis[p[i]])==1) inc(ans,1LL*coef*invm%mod*C2m%mod*pw[cur-1]%mod),--cur,DSU::link(i,p[i]);
		else if (!DSU::query(i,p[i])) inc(ans,1LL*coef*invm%mod*invm%mod*C2m%mod*pw[cur]%mod),DSU::link(i,p[i]),coef=1LL*coef*invm%mod;
		vis[i]=vis[p[i]]=1;
	} else
	{
		if (!vis[i]) coef=1LL*coef*m%mod,--cur,vis[i]=1;
	}
	return printf("%d",ans),0;
}

D - Binary Representations and Queries

在线代课上思考这道题很自然就想到做法了的说

首先不难发现每次转移就是一个线性变换,我们设一对数\((t_0,t_1)\),其中\(t_0,t_1\)只有第\(k\)位一个为\(0\)一个为\(1\),其它位相同

构造矩阵\(D=\left[\begin{matrix} a_{t_0}\\a_{t_1}\end{matrix}\right]\),不难发现若\(x_j=k,y_j=0\),则一次转移相当于左乘上\(A=\left[\begin{matrix} 1 \ 0\\1 \ 1\end{matrix}\right]\),若\(x_j=k,y_j=1\),则一次转移相当于左乘上\(B=\left[\begin{matrix} 1 \ 1\\0 \ 1\end{matrix}\right]\)

然后观察转移发现每一位对应的\(x_j\)带来的贡献独立,因此直接按位做即可,不过要注意此时也要保持按询问的顺序左乘矩阵

具体写的话就是在FFT的循环结构上稍作修改即可,复杂度\(O(2^3\times n\times 2^n)\)

#include<cstdio>
#include<cstring>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=18,mod=998244353;
int n,q,m,a[1<<N],x,y; vector <int> v[N];
inline void inc(int& x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
struct Matrix
{
	int n,m,mat[2][2];
	inline Matrix(CI N=2,CI M=2)
	{
		n=N; m=M; memset(mat,0,sizeof(mat));
	}
	inline int* operator [] (CI x) { return mat[x]; }
	friend inline Matrix operator * (Matrix A,Matrix B)
	{
		Matrix C(A.n,B.m); RI i,j,k; for (i=0;i<C.n;++i)
		for (j=0;j<C.m;++j) for (k=0;k<A.m;++k)
		inc(C[i][j],1LL*A[i][k]*B[k][j]%mod); return C;
	}
};
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i,j,k,p; for (scanf("%d%d",&n,&q),m=1<<n,i=0;i<m;++i) scanf("%d",&a[i]);
	for (i=1;i<=q;++i) scanf("%d%d",&x,&y),v[x].push_back(y);
	for (i=1,p=0;i<m;i<<=1,++p)
	{
		Matrix coef,A,B; coef[0][0]=coef[1][1]=1;
		A[0][0]=A[1][0]=A[1][1]=B[0][0]=B[0][1]=B[1][1]=1;
		for (int t:v[p]) coef=(t?B:A)*coef;
		for (j=0;j<m;j+=(i<<1)) for (k=0;k<i;++k)
		{
			Matrix D(2,1); D[0][0]=a[j+k]; D[1][0]=a[i+j+k];
			D=coef*D; a[j+k]=D[0][0]; a[i+j+k]=D[1][0];
		}
	}
	for (i=0;i<m;++i) printf("%d ",a[i]);
	return 0;
}

Postscript

看了下E好像要后缀数组的姿势啊,感觉忘得差不多了等以后再来补吧

posted @ 2022-10-19 23:44  空気力学の詩  阅读(47)  评论(0编辑  收藏  举报