【Atcoder】AGC030 B-F简要题解

B.Tree Burning

最后一定存在一个分界点,满足在分界点两边的点反复横跳(点多的一边会先走几步),记录所有点顺时针/逆时针到原点的前缀和,枚举分界点取 max ⁡ \max max即可。


*C.Coloring Torus

K ≤ 500 K\leq 500 K500直接构造 K × K K\times K K×K的矩阵,第 i i i行填 i i i

K > 500 K>500 K>500,发现构造奇数行 c i , j = ( i + j ( m o d n ) ) + 1 c_{i,j}=(i+j\pmod n)+1 ci,j=(i+j(modn))+1,偶数行 c i , j = ( i + j ( m o d ) ) + n + 1 c_{i,j}=(i+j\pmod)+n+1 ci,j=(i+j(mod))+n+1一定合法(若 c i , j > K c_{i,j}>K ci,j>K,直接 − n -n n)。所以 n = 2 ⌈ K 4 ⌉ n=2\lceil\frac{K}{4}\rceil n=24K(强制有偶数行)


D.Inversion Sum

期望的DP十分套路:
f [ i ] [ j ] f[i][j] f[i][j]表示 a i > a j a_i>a_j ai>aj的概率,每次操作涉及到的转移是 O ( n ) O(n) O(n)的。

求出总期望再乘上 2 Q 2^Q 2Q即可。


*E.Less than 3

考虑可以被翻转的 1 1 1需要满足的条件,要么左右都是 1 1 1,要么有且只有一边与一个 0 0 0相邻。
发现原序列中存在的所有连续的 0 0 0(或连续的 1 1 1)的这些段永远存在(不可能 101 101 101变成 111 111 111),且可能会在原来的若干段中分割出新的不同的子段(如 111 → 101 111\to101 111101,多了一个 0 0 0段)

接着需要一步神仙转化:

01 01 01之间加一个红色隔板, 10 10 10之间加一个蓝色隔板,序列就是一堆红蓝相间的隔板,且设两端都有无穷个隔板。
每次取反相当于把某个隔板移动一位,且隔板间不能互相跨越,即相对位置保持不变。
所以知道了最终态的每个隔板的位置后,所有初始隔板是和其一一对应的,最小的移动代价就是对应隔板的位置之差。

枚举在序列开头/末尾多插了 i ( 0 ≤ i ≤ n ) i(0\leq i\leq n) i(0in)个隔板的代价,取 min ⁡ \min min即可。复杂度 O ( n 2 ) O(n^2) O(n2)


*F.Permutation and Minimum

巧妙的值域上DP

不考虑 A 2 i − 1 ≠ − 1 A_{2i-1}\neq -1 A2i1̸=1 A 2 i ≠ − 1 A_{2i}\neq -1 A2i̸=1的位置,剩下的为 ( − 1 , x ) , ( x , − 1 ) , ( − 1 , − 1 ) (-1,x),(x,-1),(-1,-1) (1,x),(x,1),(1,1)的情况。

由于 1 − 2 N 1-2N 12N的数都会被选用,考虑值域序列 1 − 2 N 1-2N 12N min ⁡ ( A 2 i − 1 , A 2 i ) \min(A_{2i-1},A_{2i}) min(A2i1,A2i) max ⁡ ( A 2 i − 1 , A 2 i ) \max(A_{2i-1},A_{2i}) max(A2i1,A2i)的匹配类似于括号序列:

  • 对于 ( − 1 , − 1 ) (-1,-1) (1,1),处理时忽略它们的位置关系,相当于一个括号匹配,最后乘上 ( − 1 , − 1 ) (-1,-1) (1,1)对数的阶乘
  • 对于 ( − 1 , x ) , ( x , − 1 ) (-1,x),(x,-1) (1,x),(x,1),相当于其中一个括号是带标号的,分别考虑 x x x为左,右括号的情况即可。

具体来说,倒序DP(正序似乎也可以,不过倒着更好理解),设 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示完值域 i − 2 N i-2N i2N,还有 j j j个普通右括号, k k k个带标号右括号未匹配的方案数,具体转移见代码。

注意这里并非传统意义上的括号匹配——所有右括号是等价的(包括有标号的),同一个左括号( min ⁡ ( A 2 i − 1 , A 2 i ) \min(A_{2i-1},A_{2i}) min(A2i1,A2i))匹配不同右括号的情况是等价的( B i B_i Bi一样),所以实际上DP的是左括号的不同分配方式。

#include
using namespace std;
const int mod=1e9+7;
typedef long long ll;

int n,a[305],ans,m,num,frc=1;
int f[2][305][305],typ[605];

inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}

int main(){
	int i,j,k,pr=0;
	scanf("%d",&n);m=n+n;
	for(i=1;i<=m;++i) scanf("%d",&a[i]);
	for(i=1;i<=m;i+=2)
	  if((a[i]==-1)||(a[i+1]==-1)){
		num+=(((~a[i])||(~a[i+1]))^1);
		if(~a[i]) typ[a[i]]=1;if(~a[i+1]) typ[a[i+1]]=1;
	  }else typ[a[i]]=typ[a[i+1]]=2;
	for(i=1;i<=num;++i) frc=(ll)frc*i%mod; 
	f[0][0][0]=1;num=0;
	for(i=m;i;--i) if(typ[i]!=2){
		pr^=1;memset(f[pr],0,sizeof(f[pr]));
		for(j=0;j<=num;++j)
		  for(k=0;j+k<=num;++k) if(f[pr^1][j][k]){
			 if(typ[i]){
			 	ad(f[pr][j][k+1],f[pr^1][j][k]);
			 	if(j) ad(f[pr][j-1][k],(ll)f[pr^1][j][k]%mod);
			 }else{
			 	ad(f[pr][j+1][k],f[pr^1][j][k]); 
			 	if(k) ad(f[pr][j][k-1],(ll)k*f[pr^1][j][k]%mod);

			 	if(j) ad(f[pr][j-1][k],f[pr^1][j][k]);
			 }
		  }
		num=min(num+1,m>>1);
	}
	printf("%d",(ll)frc*f[pr][0][0]%mod);
}

你可能感兴趣的:(妙,atcoder)