【Atcoder】AGC021 B-F简要题解

版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ https://blog.csdn.net/corsica6/article/details/88737054

一场9h才做完的AGC,
我太菜了,哇的一声哭出来 (。﹏。)


*B.Holes

若所有点贡献则两个端点概率各为 0.5 0.5 ,其余点概率为 0 0

注意到 R = 1 0 1 0 1 0 10 R=10^{10^{10^{10}}} 实在太大了——求所有点的凸包,可以只计算凸包外的点到每个点的概率(凸包内的点可以忽略不计)。
凸包上每个点的概率即 π θ i 2 π ( θ i \dfrac{\pi-\theta_i}{2\pi}(\theta_i 是点 i i 内夹角)

还有一种 O ( n 2 log n ) O(n^2\log n) 的很好写的算法,戳这里


*C.Tiling

划分成 4 × 4 4\times 4 的小方格,若 n , m n,m 为奇,单独用 1 × 2 , 2 × 1 1\times 2,2\times 1 去填补最后一行/列。
注意特判右下角 3 × 3 3\times 3 的方格(3同3异换成2同2异):
<>^
^*v
v<>

code from yfzcsc

#include<bits/stdc++.h>
using namespace std;
char s[1010][1010];
int n,m,a,b;
int main(){
    scanf("%d%d%d%d",&n,&m,&a,&b);
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)s[i][j]='.';
    if(n&1)for(int i=1;i<m&&a;i+=2)s[n][i]='<',s[n][i+1]='>',a--;
    if(m&1)for(int i=1;i<n&&b;i+=2)s[i][m]='^',s[i+1][m]='v',b--;
    for(int i=1;i<n;i+=2)
        for(int j=1;j<m;j+=2){
            if(a>=2){
                a-=2;
                s[i][j]='<',s[i][j+1]='>';
                s[i+1][j]='<',s[i+1][j+1]='>';
            } else if(b>=2){
                b-=2;
                s[i][j]='^',s[i][j+1]='^';
                s[i+1][j]='v',s[i+1][j+1]='v';
            } else if(a&&b&&i==n-2&&j==m-2){
                a--,b--;
                s[i][j]='<',s[i][j+1]='>',s[i][j+2]='^';
                s[i+1][j]='^',s[i+1][j+2]='v';
                s[i+2][j]='v',s[i+2][j+1]='<',s[i+2][j+2]='>';
            } else if(a){
                a--;
                s[i][j]='<',s[i][j+1]='>';
            } else if(b){
                b--;
                s[i][j]='^',s[i+1][j]='v';
            }
        }
    if(a||b)return puts("NO"),0;
    puts("YES");
    for(int i=1;i<=n;++i)puts(s[i]+1);
}

D.Reversed LCS

dp不解释


*E.Ball Eat Chameleons

zz了。想了好久。

一只变色龙最终变成红色的方案有两种:

  • c n t R = c n t B cnt_R=cnt_B ,且以 B B 结尾
  • c n t R &gt; c n t B cnt_R&gt;cnt_B

考虑看成求解二维平面上 ( 0 , 0 ) ( R , B ) (0,0)\to (R,B) 的合法路径数,分类讨论如下:

  • R = B R=B ,则最后一个球必然是蓝色的( ( R , B 1 ) ( R , B ) (R,B-1)\to (R,B) ),贪心确定合法:取出 n n 对有序的 R B RB ,剩下的全部分给其中一对。可知答案路径不经过 y x R n + 1 y-x\geq R-n+1 (即 max ( y x ) R n \max(y-x)\leq R-n , max ( y x ) \max(y-x) 表示剩下的不能与左边的 R R 配对的 B B 的个数)
  • R B R\neq B ,贪心确定合法:取 n ( R B ) n-(R-B) 对有序的 R B RB R B R-B R R ,剩下的全部分给其中一个 R R 。同样剩下的蓝球要 B ( n ( R B ) ) = R n \leq B-(n-(R-B))=R-n ,故答案路径不经过 y x R n + 1 y-x\geq R-n+1

卡特兰数计算:

扫描二维码关注公众号,回复: 5725569 查看本文章

R = B R=B 的答案: ( R + B 1 R ) ( R + B 1 2 R n + 1 ) \dbinom{R+B-1}{R}-\dbinom{R+B-1}{2R-n+1}

R B R\neq B 的答案: ( R + B R ) ( R + B 2 R n + 1 ) \dbinom{R+B}{R}-\dbinom{R+B}{2R-n+1}


*F.Trinity

MDyfzcsc的题解真心看不懂,还是wxh010910的题解良心!

d p [ i ] [ j ] dp[i][j] 为强制每行都有格子染黑的 i i j j 列矩阵的方案数。

每次列数 j + 1 j+1 拓展, d p [ i ] [ j ] d p [ i + k ] [ j + 1 ] dp[i][j]\to dp[i+k][j+1] (枚举 k k 表示新出现了 k k 行,这些行的第一个被染黑的格子在 j + 1 j+1 列出现),分类讨论:

  • k = 0 k=0 ,在第 j + 1 j+1 列随意选0,1,2个端点,方案数 1 + i + ( i 2 ) 1+i+\binom{i}{2}
  • k &gt; 0 k&gt;0 ,放置完 k k 行后,可以在这些行之上选一个属于原来 i i 行中的行 p p ( p , j + 1 ) (p,j+1) 染色,使得 B j + 1 = p B_{j+1}=p ,也可以在这些行之下选一个属于原来 i i 行中的行 q q ( q , j + 1 ) (q,j+1) 染色,使得 C j + 1 = q C_{j+1}=q ,那么可以多加 2 2 行分别代表 B j + 1 , C j + 1 B_{j+1},C_{j+1} ,同时给新增的 k k 行也增加 2 2 行表示 k k 行中的最小/大行,方案数即 ( i + k + 2 k + 2 ) \binom{i+k+2}{k+2}
    正确性:每一种组合都对应着 k k 新增的两行是否选中的全局对应 B j + 1 , C j + 1 B_{j+1},C_{j+1} 的两行的某种方案。稍微有点感性…

a n s = i = 0 n ( n i ) d p [ i ] [ m ] ans=\sum \limits_{i=0}^n \binom{n}{i}dp[i][m]

合并是个卷积,复杂度 O ( n m log n ) O(nm\log n)

#include<bits/stdc++.h>
#define pb push_back
#define gc getchar 
using namespace std;
const int N=5e4+10,mod=998244353,g=3;
typedef long long ll;
typedef double db;

int n,m,ans,f[N],h[N],t[N],frc[N],nv[N],ivg;

char cp;
template<class T>inline void rd(T &x)
{
	cp=gc();x=0;int f=0;
	for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
	if(f) x=-x;
}

inline int fp(int x,int y)
{
	int re=1;
	for(;y;y>>=1,x=(ll)x*x%mod)
	  if(y&1) re=(ll)re*x%mod;
	return re;
}

inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;} 
inline int C(int n,int m)
{
	if(m>n) return 0;
	return (ll)frc[n]*nv[m]%mod*(ll)nv[n-m]%mod;
}

namespace poly{
   int rv[N],L,len;
   
   inline void ntt(int *e,int pr)
   {
   	   int i,j,k,ix,iy,pd,ori,G=pr?g:ivg;
   	   for(i=1;i<len;++i) if(i<rv[i]) swap(e[i],e[rv[i]]);
   	   for(i=1;i<len;i<<=1){
   	      ori=fp(G,(mod-1)/(i<<1));
   	      for(j=0;j<len;j+=(i<<1)){
   	      	for(pd=1,k=0;k<i;++k,pd=(ll)pd*ori%mod){
   	      		ix=e[j+k];iy=(ll)pd*e[j+i+k]%mod;
   	      		e[j+k]=ad(ix,iy);e[i+j+k]=dc(ix,iy);
			}
		  }
	   }
	   if(pr) return;
	   G=fp(len,mod-2);
	   for(i=0;i<len;++i) e[i]=(ll)e[i]*G%mod;
   }
   
   void mul(int *f,int *g,int n,int m)
   {
   	   int i;
   	   for(L=0,len=1;len<=n+m;len<<=1) L++;
	   for(i=1;i<len;++i) rv[i]=(rv[i>>1]>>1)|((i&1)<<(L-1));
	   for(i=n+1;i<len;++i) f[i]=0;
   	   for(i=m+1;i<len;++i) g[i]=0;
   	   ntt(f,1);ntt(g,1);
   	   for(i=0;i<len;++i) f[i]=(ll)f[i]*g[i]%mod;
	   ntt(f,0); 
   }
}
using namespace poly;

int main(){
	int i,j,x,y;ivg=fp(g,mod-2);
	rd(n);rd(m);
	f[0]=frc[0]=nv[0]=frc[1]=nv[1]=1;
    for(i=2;i<n+3;++i)
	  frc[i]=(ll)frc[i-1]*i%mod,
	  nv[i]=(ll)(mod-mod/i)*nv[mod%i]%mod;
	for(i=2;i<n+3;++i) nv[i]=(ll)nv[i-1]*nv[i]%mod;
	for(i=1;i<=n;++i) h[i]=nv[i+2];
	for(L=0,len=1;len<=(n<<1);len<<=1) L++;
	for(i=1;i<len;++i) rv[i]=((rv[i>>1]>>1)|((i&1)<<(L-1)));
	ntt(h,1);
	for(j=1;j<=m;++j){
		for(i=0;i<=n;++i) t[i]=(ll)nv[i]*f[i]%mod;
		for(i=n+1;i<len;++i) t[i]=0;
		ntt(t,1);
		for(i=0;i<len;++i) t[i]=(ll)h[i]*t[i]%mod;
		ntt(t,0);
		for(i=0;i<=n;++i)
		 f[i]=ad((ll)(1+C(i+1,2))*f[i]%mod,(ll)t[i]*frc[i+2]%mod);
	}
	for(i=0;i<=n;++i) ans=ad(ans,(ll)C(n,i)*f[i]%mod);
	printf("%d",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/88737054
今日推荐