其他分享
首页 > 其他分享> > [Contest on 2022.6.20] (

[Contest on 2022.6.20] (

作者:互联网

\(\cal T_1\) 益智游戏

Description

一个 \(n × n\) 棋盘的每个格子被继续划分为了一个 \(2 × 2\) 的小棋盘,每个小棋盘中放了一个 \(1 × 2\) 的多米诺骨牌。

每个小棋盘有个修改代价,你需要花费最小的修改代价使得所有填了多米诺骨牌的小格子中心之间的距离不为 \(\sqrt 2\).

\(1 \leqslant n \leqslant 2000\).

多米诺骨牌从左到右编号为 \(1,2,3,4\):.

Solution

先考虑只有 \(1,2\) 骨牌的情况,容易发现一定是骨牌 \(1\) 为一段前缀,骨牌 \(2\) 为一段后缀,且随着行数增加,前缀长度不增。这个图形可以 \(\mathtt{dp}\):设 \(dp(i,j)\) 为前 \(i\) 行,第 \(i\) 行分界线在 \(j\) 处的最小代价即可做到 \(\mathcal O(n^2)\).

考虑所有骨牌,每一行从左到右一定是先骨牌 \(4\),后面跟一段骨牌 \(1\) 骨牌 \(3\),最后一段骨牌 \(2\)。同样地,我们可以从四个方向来看这个图形,都遵循上述规律,所以它最后会长成这样的形态:

解释一下,由于每一列从上到下一定是先骨牌 \(1\),后面跟一段骨牌 \(2\) 骨牌 \(4\),最后一段骨牌 \(3\),所以对于所有行而言,一定是先 \(4\rightarrow 1\rightarrow 2\),再 \(4\rightarrow 3\rightarrow 2\),于是可以将图形划分为两个部分,每个部分只有三种骨牌。更进一步,以 \(4\rightarrow 1\rightarrow 2\) 为例,由于骨牌 \(1\) 不能在任何除了骨牌 \(1\) 的骨牌类型下面。嗯,这个表述也是极具现实意义的。

所以假设第 \(i\) 行骨牌 \(1\) 的区间范围为 \([l_i,r_i]\),那么下一行的区间范围一定有 \(l_{i+1}\geqslant l_i,r_{i+1}\leqslant r_i\),我们惊喜地发现,如果以 \(4\rightarrow 1\rightarrow 2\) 类型最后一行的骨牌 \(1\) 区间 \([L,R]\) 中任意一个位置纵向划分,这个类型就可以规约到上述两种骨牌的情况,可以使用 \(\mathcal O(n^2)\) 的 \(\mathtt{dp}\) 解决。最后把四种类型拼起来即可。

另外还要注意的是,在上下拼接的时候骨牌 \(2,4\) 不能在同一列。

再详细地讲一下 \(\mathtt{dp}\),对于左上角的部分是设 \(dp(i,j)\) 为填左上角 \(i\) 行 \(j\) 列的格子,且符合要求的最小修改代价,转移加入一行或一列。

Code

# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)

template <class T>
inline T read(const T sample) {
    T x=0; char s; bool f=0;
    while(!isdigit(s=getchar())) f|=(s=='-');
    for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
    return f? -x: x;
}
template <class T>
inline void write(T x) {
    static int writ[50], w_tp=0;
    if(x<0) putchar('-'), x=-x;
    do writ[++w_tp]=x-x/10*10, x/=10; while(x);
    while(putchar(writ[w_tp--]^48), w_tp);
}

# include <cstring>
# include <iostream>
using namespace std;

# define int long long

const int maxn = 2005;

int n, c[maxn][maxn], a[maxn][maxn];
int dp[4][maxn][maxn], f[4][maxn][maxn];

int val(int x,int y,int v) {
    return a[x][y]==v? 0: c[x][y];
}

signed main() {
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    n=read(9);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j) a[i][j]=read(9);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j) c[i][j]=read(9);
    int ans = 1e18;
    for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)
        f[0][i][j] = f[0][i-1][j]+val(i,j,1),
        f[3][i][j] = f[3][i][j-1]+val(i,j,4);
    for(int i=n;i;--i) for(int j=n;j;--j)
        f[1][i][j] = f[1][i][j+1]+val(i,j,2),
        f[2][i][j] = f[2][i+1][j]+val(i,j,3);
    for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)
        dp[0][i][j] = min(dp[0][i-1][j]+f[3][i][j],dp[0][i][j-1]+f[0][i][j]);
    for(int i=1;i<=n;++i) for(int j=n;j;--j)
        dp[1][i][j] = min(dp[1][i-1][j]+f[1][i][j],dp[1][i][j+1]+f[0][i][j]);
    for(int i=n;i;--i) for(int j=1;j<=n;++j)
        dp[2][i][j] = min(dp[2][i+1][j]+f[3][i][j],dp[2][i][j-1]+f[2][i][j]);
    for(int i=n;i;--i) for(int j=n;j;--j)
        dp[3][i][j] = min(dp[3][i+1][j]+f[1][i][j],dp[3][i][j+1]+f[2][i][j]);
    for(int i=0;i<=n;++i) for(int j=0;j<=n;++j)
        ans = min(ans, dp[0][i][j]+dp[1][i][j+1]+dp[2][i+1][j]+dp[3][i+1][j+1]);
    print(ans,'\n');
    return 0;   
}

\(\cal T_2\) 华为

Description

有两个变量 \(x, y\),初始有 \(x ← 0,y ← 0\)。每个时刻唐芯会选择恰好一个变量令其加 \(1\),更具体地,有 \(p\) 的概率令 \(x ← x + 1\),\(q\) 的概率令 \(y ← y + 1\),且有 \(p + q = 1\).

你需要对 \(t\) 组 \(X_i , Y_i\),求第一次满足 \(x ≡ X_i \pmod n\) 且 \(y ≡ Y_i \pmod m\) 的期望时 间。

为了避免精度误差,答案对 \(998244353\) 取模。

\(2\leqslant n,m\),\(n\leqslant 10^9,m\leqslant 400,t\leqslant 500\).

Solution

一些闲话:今天的题目是多项式专场吗?可是我多项式烂得不行欸

标签:20,Contest,int,long,cdot,maxn,骨牌,2022.6,dp
来源: https://www.cnblogs.com/AWhiteWall/p/16396204.html