1. 程式人生 > 實用技巧 >【AtCoder】AtCoder Grand Contest 046 解題報告(希望不會又咕掉)

【AtCoder】AtCoder Grand Contest 046 解題報告(希望不會又咕掉)

點此進入比賽

\(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;//輸出答案
}