zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    线性变换 矩阵
    【安全】安全测试基本切入点
    【产品】汉语言基础对产品经理的重要性
    【Selenium】自动化调试后C盘越来越大
    【记录】让人淡疼的BUG之参数传送错误
    【转载】Java 升级到jdk7后DbVisualizer 6 启动空指针的处理方案
    cocoapods 报错
    mac 版 Pycharm 激活
    Foxmail添加gmail密码错误
    Apple 相关官方地址
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/AtCoderAGC046.html
Copyright © 2011-2022 走看看