把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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

点此进入比赛

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

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

\[\frac{lcm(360,x)}{x}=\frac{\frac{360\times 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\n",360/gcd(360,n)),0;//直接输入+输出
}

\(B\):Extension(点此看题面

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

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

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

\[f_{i,b}=f_{i-1,b}\times b \]

\[f_{a,j}=f_{a,j-1}\times a \]

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

\[f_{i,j}=f_{i,j-1}\times i+f_{i-1,j}\times j-f_{i-1,j-1}\times(i-1)\times(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\n",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\n",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\n",t),0;
}
posted @ 2020-07-21 16:13  TheLostWeak  阅读(206)  评论(0编辑  收藏  举报