【Atcoder】AGC030 B-F简要题解

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

B.Tree Burning

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


*C.Coloring Torus

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

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


D.Inversion Sum

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

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


*E.Less than 3

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

接着需要一步神仙转化:

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

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


*F.Permutation and Minimum

巧妙的值域上DP

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

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

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

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

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

#include<bits/stdc++.h>
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);
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/88892612