zoukankan      html  css  js  c++  java
  • 「SOL」矩阵游戏 (2021省选A卷)

    考前感觉啥都复习了
    考后感觉啥啥都不会


    # 题面

    > Link 洛谷 P7515


    # 解析

    最难处理的是 (a_{ij})([0,10^6]) 的限制(因为有这个限制所以我 (n,mle3) 都不会做……)。

    假如没有这个限制,显然我们可以随便构造出一组解 ({a'_{ij}}),下面给出一种构造方法:

    • 固定最后一行和最后一列都是 (0)
    • 从下到上从左到右依次确定 (a'_{ij})(不需要满足范围限制,注意会爆 int

    然后考虑如何把 ({a'_{ij}}) 修修补补使它满足范围限制。

    这里的构造非常巧妙,我们可以将第 (i) 行的偶数列加上 (p_i),奇数列减去 (p_i),这样操作后每一个 (2 imes2) 的矩形的和都不变;将第 (j) 列的偶数行加上 (q_j),奇数行减去 (q_j) 同理。

    于是我们要求:

    [forall iin[1,n]land jin[1,m], 0le a'_{ij}+(-1)^ip_i+(-1)^jq_jle 10^6 ]

    差不多转化成了下面这种不等式

    [L_{ij}le p_ipm q_jle R_{ij} ]

    这个问题似乎也并不好求解,但是我们会用差分约束求解

    [Lle p-qle R ]

    怎么消除掉“两个变量之和”的不等式?考虑改变我们的构造方式,原本我们构造的方式是

    [egin{bmatrix} -p_1&+p_1&-p_1&cdots\ -p_2&+p_2&-p_2&cdots\ -p_3&+p_3&-p_3&cdots\ vdots&vdots&vdots&ddots end{bmatrix}+egin{bmatrix} -q_1&-q_2&-q_3&cdots\ +q_1&+q_2&+q_3&cdots\ -q_1&-q_2&-q_3&cdots\ vdots&vdots&vdots&ddots end{bmatrix} ]

    我们发现,在 (i,j) 奇偶性相同的位置,会出现变量之和的限制,于是我们稍作调整:

    [egin{bmatrix} -p_1&+p_1&-p_1&cdots\ +p_2&-p_2&+p_2&cdots\ -p_3&+p_3&-p_3&cdots\ vdots&vdots&vdots&ddots end{bmatrix}+egin{bmatrix} +q_1&-q_2&+q_3&cdots\ -q_1&+q_2&-q_3&cdots\ +q_1&-q_2&+q_3&cdots\ vdots&vdots&vdots&ddots end{bmatrix} ]

    不难发现,这样构造仍然可以让 (2 imes2) 的矩形和不变。而这样构造的好处就在于,对应位置的正负性总相反。

    然后就可以非常愉快地差分约束。

    如果有负环就无解。有负权边,跑 SPFA?反正我会被卡常,看题解发现可以直接 bellman-ford,而且因为是完全二分图,可以用邻接矩阵存边。

    跑得特别慢……

    最后还有一点小问题。为什么这样是对的呢?我们感觉这个算法看起来就非常对…… 给出一个不是很严谨的证明。

    证明

    起初对这道题有一个非常暴力的想法:如果确定了 ({a_{ij}}) 的最后一行和最后一列,那么可以唯一确定矩阵 ({a_{ij}})(在忽略范围限制的前提下)。也就是说,我们可以用最后一行、最后一列代表一个矩阵。

    假设存在合法解 ({a_{ij}}),那么通过 (a_{im}pm p_i)(a_{nj}pm q_j),可以得到任意一种 (a'_{im},a'_{nj}),也即可以得到任意一种不符合范围限制的矩阵 ({a'_{ij}})

    为什么会存在多解呢?因为实际上最后一行和最后一列只能提供 (n+m-1) 个方程,而我们有 (n+m) 个变量。


    # 源代码

    /*Lucky_Glass*/
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    typedef long long llong;
    const int N = 305, M = N * N, B = 1e6;
    const llong INF = 0x3f3f3f3f3f3f3f3fll;
    #define con(typ) const typ &
    inline int rin(int &r) {
    	int b = 1, c = getchar(); r = 0;
    	while ( c < '0' || '9' < c ) b = c == '-' ? -1 : b, c = getchar();
    	while ( '0' <= c && c <= '9' ) r = (r * 10) + (c ^ '0'), c = getchar();
    	return r *= b;
    }
    void write(con(int) w) {
    	if ( w < 0 ) putchar('-'), write(-w);
    	else if ( w < 10 ) putchar('0' + w);
    	else write(w / 10), putchar('0' + w % 10);
    }
    
    int n, m, nedg;
    int mtb[N][N];
    llong mta[N][N], edga[N][N], edgb[N][N];
    llong dis[N << 1];
    
    bool relax(con(int) u, con(llong) len, con(int) v) {
    	if ( dis[v] > dis[u] + len ) {
    		dis[v] = dis[u] + len;
    		return true;
    	}
    	return false;
    }
    bool bellmanFord() {
    	int cnt = 0; bool tag = false;
    	for (int i = 1; i <= n + m; i++)
    		dis[i] = 0;
    	do {
    		cnt++, tag = false;
    		if ( cnt >= n + m ) return false;
    		for (int u = 1; u <= n + m; u++)
    			if ( u <= n )
    				for (int v = 1; v <= m; v++)
    					tag |= relax(u, edga[u][v], v + n);
    			else
    				for (int v = 1; v <= n; v++)
    					tag |= relax(u, edgb[u - n][v], v);
    	} while ( tag );
    	return true;
    }
    void clean() {
    	nedg = 0;
    	fill(dis, dis + n + m + 1, INF);
    }
    int main() {
    	int ncas; rin(ncas);
    	while ( ncas-- ) {
    		rin(n), rin(m);
    		clean();
    		for (int i = 1; i < n; i++)
    			for (int j = 1; j < m; j++)
    				rin(mtb[i][j]);
    		for (int i = n; i; i--)
    			for (int j = m; j; j--)
    				if ( i == n || j == m ) mta[i][j] = 0;
    				else mta[i][j] = mtb[i][j] - mta[i + 1][j + 1]
    							   - mta[i + 1][j] - mta[i][j + 1];
    		for (int i = 1; i <= n; i++)
    			for (int j = 1; j <= m; j++)
    				if ( (i & 1) ^ (j & 1) )
    					edga[i][j] = mta[i][j], edgb[j][i] = B - mta[i][j];
    				else
    					edgb[j][i] = mta[i][j], edga[i][j] = B - mta[i][j];
    		if ( !bellmanFord() ) {printf("NO
    "); continue;}
    		printf("YES
    ");
    		for (int i = 1; i <= n; i++)
    			for (int j = 1; j <= m; j++) {
    				llong now = mta[i][j];
    				if ( (i & 1) ^ (j & 1) ) now += dis[i] - dis[j + n];
    				else now += dis[j + n] - dis[i];
    				write((int)now), putchar(j == m ? '
    ' : ' ');
    			}
    	}
    	return 0;
    }
    

    THE END

    Thanks for reading!

    你走吧 趁着天色未暗
    你走吧 深知道阻且难
    我献上明月一盏 照满河山

    ——《山遥路远(人声本家)》By ChiliChill

    PS. 省选之后再听这首歌又有不一样的感动,似乎是我将此歌唱给谁人,又像是谁人唱予我。

    欢迎转载٩(๑❛ᴗ❛๑)۶,请在转载文章末尾附上原博文网址~
  • 相关阅读:
    03 java中的基本数据类型和运算符
    02 Eclipse安装
    01 HelloWorld
    express不是内部或外部命令
    win10 内存或系统资源不足,无法打开PPT
    win 10中解决“此文件在另外一个进程中运行”的问题
    后台查找密码暴力破解
    DVWA--全等级暴力破解(Burte Force)
    DVWA简单搭建
    破解版
  • 原文地址:https://www.cnblogs.com/LuckyGlass-blog/p/14666495.html
Copyright © 2011-2022 走看看