1 条题解

  • 0
    @ 2026-9-2 0:59:14

    构造题。

    首先可以非常容易想到 n=2n=2 的解法,把 11 看作黑球,22 看作白球,xx 作为第一列,yy 作为第二列,n+1n+1 作为多出的一列:

    1. 初始状态。

      121121
      122212
      
      
    2. xx 中的黑球个数 cxcx 统计下来,把 yycxcx 个球移动到 n+1n+1

      121121
      12
      2122
      
    3. xx 中的黑球放到 yy,白球放到 n+1n+1

      
      121111
      212222
      
    4. n+1n+1 中的 mcxm-cx 个原属于 xx 的白球移动回 xx

      22
      121111
      2122
      
    5. yy 中的 cxcx 个原属于 xx 的黑球移动回 xx。(xx 现在前面是白球,后面是黑球)

      221111
      12
      2122
      
    6. n+1n+1 中的 cxcx 个原属于 yy 的球移动回 yy

      221111
      122212
      
      
    7. xx 中的 cxcx 个黑球放到 n+1n+1xx 现在只有白球)

      22
      122212
      1111
      
    8. yy 中的黑球放到 n+1n+1,白球放到 xx

      222222
      
      111111
      
    9. n+1n+1 中的黑球放回 yy

      222222
      111111
      
      

    上面其实就是将第二列作为黑球的储存地,第三列作为白球的储存地。

    其实和正解很接近。

    不妨利用分治思想,分治一个 midmid,将小于等于 midmid 的看作黑球,大于 midmid 的看作白球。然后把颜色在 [l,r][l,r] 内的两两相邻的做 n=2n=2 的操作。

    但是我们会发现,黑球的个数 ww 可能会大于或小于 mm,这会导致在进行如上相同操作时,yy 列不够多放到 n+1n+1 列。

    所以当 w<mw<m 时,我们随机选择一些白球染成黑球;当所以当 w>mw>m 时,我们随机选择一些黑球染成白球。

    因为我们会在下一次分治时进一步分类,所以不用担忧黑球和白球会混在一起。

    最坏情况是 7nmlogn8000007nm\log n\le800000,可以过。

    #include <cstdio>
    #include <vector>
    using namespace std;
    #define ll long long
    #define N 60
    #define M 410
    #define debug false
    ll n, m;
    ll top[N];
    ll num[N][M];	// 某一行某种颜色的个数
    ll a[N][M];		// 给定的颜色分布
    bool b[N][M];	// 黑白球规定
    
    
    struct node {
    	ll x, y;
    } ans[820010];
    ll cnt;
    
    
    void show() {
    	for(ll i = 1; i <= n+1; i++) {
    		for(ll j = 1; j <= top[i]; j++) {
    			printf("%lld ", a[i][j]);
    		}
    		printf("\n");
    	}
    	printf("\n");
    }
    
    void mov(ll x, ll y) {
    	ans[++cnt].x = x;
    	ans[cnt].y = y;
    	
    	a[y][++top[y]] = a[x][top[x]--];
    	
    	if(debug) show();
    }
    
    void fun(ll x, ll y, ll mid) {
    	ll numx = 0, numy = 0;
    	for(ll i = 1; i <= m; i++) {
    		if(a[x][i] <= mid) numx++, b[x][i] = 1;
    		else b[x][i] = 0;
    		if(a[y][i] <= mid) numy++, b[y][i] = 1;
    		else b[y][i] = 0;
    	}
    	
    	// 1.如果太多黑球则取反
    	if(numx + numy > m) {
    		numx = m - numx, numy = m - numy;
    		for(ll i = 1; i <= m; i++) b[x][i] ^= 1, b[y][i] ^= 1;
    	}
    	
    	// 2.如果太少则添加虚拟黑球(把黑球放到y)
    	for(ll i = 1; i <= m; i++) {
    		if(!b[x][i] && numx + numy < m) {
    			numx++;
    			b[x][i] = 1;
    		}
    	}
    	
    	// 3.把y中的numx个取到n+1中
    	if(debug) printf("把y中的numx个取到n+1中\n");
    	for(ll i = 1; i <= numx; i++) {
    		mov(y, n+1);
    	}
    	// 4.把x中的黑球放到y,白球放到n+1
    	if(debug) printf("把x中的黑球放到y,白球放到n+1\n");
    	for(ll i = 1; i <= m; i++) {
    		if(b[x][top[x]]) {
    			mov(x, y);
    		} else {
    			mov(x, n+1);
    		}
    	}
    	// 5.把n+1中的m-numx个白球放回到x
    	if(debug) printf("把n+1中的m-numx个白球放回到x\n");
    	for(ll i = 1; i <= m-numx; i++) mov(n+1, x);
    	
    	// 6.把y中的numx个黑球放回到x(x现在前面是白球,后面是黑球)
    	if(debug) printf("把y中的numx个黑球放回到x(x现在前面是白球,后面是黑球)\n");
    	for(ll i = 1; i <= numx; i++) mov(y, x);
    	
    	// 7.把n+1中的numx放回到y(y现在不变)
    	if(debug) printf("把n+1中的numx放回到y(y现在不变)\n");
    	for(ll i = 1; i <= numx; i++) mov(n+1, y);
    	
    	// 8.把x中的numx个黑球放到n+1(x现在只有白球)
    	if(debug) printf("把x中的numx个黑球放到n+1(x现在只有白球)\n");
    	for(ll i = 1; i <= numx; i++) mov(x, n+1);
    	
    	// 9.把y中的黑球放到n+1,白球放到x
    	if(debug) printf("把y中的黑球放到n+1,白球放到x\n");
    	for(ll i = 1; i <= m; i++) {
    		if(b[y][top[y]]) {
    			mov(y, n+1);
    		} else {
    			mov(y, x);
    		}
    	}
    	
    	// 10.把n+1中的黑球放回y
    	if(debug) printf("把n+1中的黑球放回y\n");
    	for(ll i = 1; i <= m; i++) {
    		mov(n+1, y);
    	}
    }
    
    void solve(ll l, ll r) {
    	if(debug) printf("===%lld %lld===\n", l, r);
    	
    	if(l == r) return;
    	
    	ll mid = (l + r) >> 1;
    	vector<ll> now;
    	for(ll i = 1; i <= n; i++) {
    		if(l <= a[i][1] && a[i][1] <= r) {
    			now.push_back(i);
    		}
    	}
    	for(ll i = 0; i < now.size() - 1; i++) {
    		fun(now[i], now[i+1], mid);
    	}
    	
    	solve(l, mid);
    	solve(mid+1, r);
    }
    
    int main() {
    	freopen("ball.in", "r", stdin);
    	freopen("ball.out", "w", stdout);
    	
    	scanf("%lld %lld", &n, &m);
    	
    	for(ll i = 1; i <= n; i++) {
    		for(ll j = 1; j <= m; j++) {
    			scanf("%lld", &a[i][j]);
    			num[i][a[i][j]]++;
    		}
    		top[i] = m;
    	}
    	
    	solve(1, n);
    	
    	printf("%lld\n", cnt);
    	for(ll i = 1; i <= cnt; i++) {
    		printf("%lld %lld\n", ans[i].x, ans[i].y);
    	}
    	
    //	show();
    }
    
    • 1

    信息

    ID
    2002
    时间
    1000ms
    内存
    512MiB
    难度
    9
    标签
    递交数
    23
    已通过
    3
    上传者