【AtCoder】AtCoder Grand Contest 046 解题报告(好吧,又咕了$E,F$)

点此进入比赛

(A):Takahashikun, The Strider(点此看题面

大致题意: 从原点出发,每次向前走(1)单位距离并转(x°),问要走多少步才能走回原点。

[frac{lcm(360,x)}{x}=frac{frac{360 imes x}{gcd(360,x)}}x=frac{360}{gcd(360,x)} ]

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
using namespace std;
int n;I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}//求gcd
int main()
{
	return scanf("%d",&n),printf("%d
",360/gcd(360,n)),0;//直接输入+输出
}

(B):Extension(点此看题面

大致题意: 给定一个(a imes b)的矩形,你每次可以加一行或者加一列,并在添加的格子中选一个涂黑。问能得到多少种(c imes d)的矩形。

(f_{i,j})表示能得到多少种(i imes j)的矩形。

先给出边界情况的显然转移:

[f_{i,b}=f_{i-1,b} imes b ]

[f_{a,j}=f_{a,j-1} imes a ]

而一般情况下的转移,考虑它必然是最终加一行或加一列得到的,而重复的情况只有最后一行、最后一列各只有一个黑格子(不能在交点处),即:

[f_{i,j}=f_{i,j-1} imes i+f_{i-1,j} imes j-f_{i-1,j-1} imes(i-1) imes(j-1) ]

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 3000
#define X 998244353
using namespace std;
int a,b,c,d,f[N+5][N+5];
int main()
{
	RI i,j;scanf("%d%d%d%d",&a,&b,&c,&d);
	for(f[a][b]=1,i=a;i<=c;++i) for(j=b;j<=d;++j) if(a==i&&b==j);else//不处理原图
		if(b==j) f[i][j]=1LL*f[i-1][j]*j%X;else if(a==i) f[i][j]=1LL*f[i][j-1]*i%X;//两种特殊边界
		else f[i][j]=(1LL*f[i-1][j]*j+1LL*f[i][j-1]*i-1LL*(i-1)*(j-1)%X*f[i-1][j-1]%X+X)%X;//一般情况转移
	return printf("%d
",f[c][d]),0;
}

(C):Shift(点此看题面

大致题意: 给定一个(01)串,你可以进行至多(k)次操作,每次把一个(0)右侧的某个(1)移到它左边。问能得到多少种本质不同的(01)串。

考虑我们把给定的(01)串转化,用(a_i)来表示第(i)(0)与第(i-1)(0)之间(1)的个数。

那么一次操作,就相当于(--a_j,++a_i(j>i)),定义这次操作为((i,j))

然后我们发现,执行操作((i,j))((j,k))等价于执行了操作((i,k))

为了避免计算重复,我们便强制对于每一个(a_i),要么增加,要么减少。

即设(f_{i,j,k})表示(DP)到第(i)个位置,进行了(j)次操作,预进行了(k)次操作(即之后还要减去(k))的方案数。

每个位置有三种转移:

  • 不操作,(f_{i+1,j,k}+=f_{i,j,k})
  • 加上(p)(f_{i+1,j+p,k+p}+=f_{i,j,k})
  • 减去(p)(f_{i+1,j,k-p}+=f_{i,j,k})

这看上去是个(O(n^4))(DP),但调整好上下界之后,由于跑不满,轻松过。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 300
#define X 998244353
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,m,a[N+5],f[N+5][N+5][N+5];char s[N+5];
int main()
{
	RI i,j,k,cnt=1;for(scanf("%s",s+1),n=strlen(s+1),i=1;i<=n;++i) s[i]^'0'?++a[cnt]:++cnt;//转化
	RI p;scanf("%d",&m),m=min(n,m),f[0][0][0]=1;//初始化
	for(i=0;i^cnt;++i) for(j=0;j<=m;++j) for(k=0;k<=m;++k) if(f[i][j][k])//枚举状态,如果状态能达到再去刷表
	{
		Inc(f[i+1][j][k],f[i][j][k]);//不操作
		for(p=m-j;p;--p) Inc(f[i+1][j+p][k+p],f[i][j][k]);//加上一个数
		for(p=min(a[i+1],k);p;--p) Inc(f[i+1][j][k-p],f[i][j][k]);//减去一个数
	}
	RI t=0;for(i=0;i<=m;++i) Inc(t,f[cnt][i][0]);return printf("%d
",t),0;//输出答案
}

(D):Secret Passage(点此看题面

大致题意: 给定一个(01)串,你可以进行任意次操作,每次删除字符串开头两个字符,再自选其中一个字符插入回字符串的任意位置。问能得到多少种本质不同的(01)串。

本来想抄题解,结果看了半天没看懂,最后只好自己去推。

结果瞎写了一个(DP),然后对着样例调了半个小时,突然就过了?!

写完发现我的(DP)状态其实设得非常(SB),但也就将就着看看吧,懒得去改了。

首先考虑如何判断一个(01)串是否能被生成,显然是贪心,从后往前记录当前匹配到原串第几个字符,如果相同就相同,否则说明需要插入一个字符。

写成(DP),就是设(f_{i,j,k})表示当前字符串长度为(n-i+1),需要插入(j)(0)(k)(1)的方案数。

转移有两种,每次在当前字符串前插入一个字符。

根据贪心思想我们要知道当前匹配到原串第几个字符,容易发现就是(s_{i+j+k})

转移方程如下:

  • 相同,(f_{i-1,j,k}+=f_{i,j,k})
  • 不同,(f_{i-1,j',k'}+=f_{i,j,k})。((s_{i+j+k}=1)(j'=j+1),否则(k'=k+1)

光这样我们无法知道还是无法知道哪些(01)串能被生成,因此还要一个(bool)数组(g_{i,j,k}),表示对前(i-1)个字符进行操作,是否能给后面的字符串插入(j)(0)(k)(1)

转移方程如下:((g_{i,j,k}=1)时转移)

  • 直接浪费掉这个字符,(g_{i+1,j,k}=1)
  • 与后一个字符配对,(g_{i+2,j',k'}=1)(注意可以保留当前字符,也可以保留后一个字符)。
  • 与之前的一个0配对,(g_{i+2,j'-1,k'}=1)
  • 与之前的一个1配对,(g_{i+1,j',k'-1}=1)

如果(f_{i,j,k})能对答案造成贡献,当且仅当(g_{i+j+k+1,j,k}=1)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 300
#define X 998244353
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,f[N+5][N+5][N+5],g[N+5][N+5][N+5];char s[N+5];
int main()
{
	RI i,j,k;scanf("%s",s+1),n=strlen(s+1);//读入
	for(f[n][0][0]=1,i=n;i;--i) for(j=n-i;~j;--j) for(k=n-i-j;~k;--k)//DP转移f
		Inc(f[i-1][j][k],f[i][j][k]),Inc(f[i-1][j+(s[i+j+k]^48)][k+(s[i+j+k]^49)],f[i][j][k]);//两种转移
	for(g[1][0][0]=1,i=1;i<=n;++i) for(j=i;~j;--j) for(k=i-j;~k;--k) g[i][j][k]&&//DP转移g
	(
		g[i+1][j][k]=1,i^n&&(g[i+2][j+(s[i]^49)][k+(s[i]^48)]=g[i+2][j+(s[i+1]^49)][k+(s[i+1]^48)]=1),//与后一个字符配对
		j&&(g[i+1][j-1+(s[i]^49)][k+(s[i]^48)]=1),k&&(g[i+1][j+(s[i]^49)][k-1+(s[i]^48)]=1)//与已有01配对
	);
	RI t=0;for(i=0;i^n;++i) for(j=n-i;~j;--j) for(k=n-i-j;~k;--k) g[i+j+k+1][j][k]&&Inc(t,f[i][j][k]);//枚举状态判断是否合法
	return printf("%d
",t),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/AtCoderAGC046.html