1. 程式人生 > 其它 >[衝刺國賽2022] 模擬賽2

[衝刺國賽2022] 模擬賽2

矩陣

題目描述

有一個 \(n\times n\) 的矩陣,初始時所有位置上都是 \(0\),每次你可以選擇若干行 \(r_1,r_2...r_p\) 和若干列 \(c_1,c_2...c_q\),把這些行列交點處的位置都變成 \(1\),即把 \((r_i,c_j),1\leq i\leq p,1\leq j\leq q\) 這些位置上的數字都變成 \(1\)

\(n\leq 3000\),詢問次數 \(\leq 26\)

解法

首先考慮簡化問題,如果只是對角線上的元素為 \(0\) 怎麼辦?由於同行同列不能同時選,可以考慮二進位制分組。對於每個二進位制位,每次把為 \(0\) 的行和為 \(1\) 的列(還有為 \(1\)

的行和為 \(0\) 的列)拿出來做,這樣一個點由於其橫縱座標不同,一定會在橫縱座標不同的那個二進位制位上被染黑,詢問次數 \(2\log n\)

本題就是多了對角線下面的那一列,嘗試把上面的做法拓展下來。我們把列奇偶分組,把奇數列和所有的行做,把偶數列和所有的行做。比如在做奇數列的時候,我們可以把 \(1,2\) 壓縮成一行,這樣就轉化到了上面的情況。

但是好像要花費 \(4\log n\) 的詢問次數,其實對角線的問題是可以做到 \(1\log n\) 的,具體來說我們把行和列都重新編號。值域為 \([0,2^{13})\),我們只把有 \(6\)\(1\) 的數取出來,給它們重編號,這樣每次只用把為 \(0\)

的行和為 \(1\) 的列拿出來做。

這是因為重編號方式不存在數位的包含關係,所以一定存在行的某個數位是 \(0\),列的某個數位是 \(1\),因為 \({13\choose 6}>1500\),所以編號也是夠用的,那麼我們就把詢問次數優化到了 \(2\log n\)

#include <cstdio>
#include <vector>
using namespace std;
const int M = 3005;
#define pb push_back
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,bit[1<<13],id[1<<13],a[M],b[M];
vector<int> A[M],B[M];
int main()
{
    freopen("matrix.in","r",stdin);
    freopen("matrix.out","w",stdout);
    n=read();read();
    for(int i=1;i<(1<<13);i++)
    {
        bit[i]=bit[i>>1]+(i&1);
        if(bit[i]==6) id[++m]=i;
    }
    for(int i=1;i<=n;i+=2)
        a[i]=b[i]=b[i+1]=id[(i+1)>>1];
    for(int t=0;t<13;t++)
    {
        k++;
        for(int i=1;i<=n;i++)
            if(!(b[i]>>t&1)) A[k].pb(i);
        for(int i=1;i<=n;i+=2)
            if(a[i]>>t&1) B[k].pb(i);
        if(A[k].empty() || B[k].empty())
            A[k].clear(),B[k].clear(),k--;
    }
    //
    for(int i=0;i<=n;i+=2)
        a[i]=b[i]=b[i+1]=id[(i+2)>>1];
    for(int t=0;t<13;t++)
    {
        k++;
        for(int i=1;i<=n;i++)
            if(!(b[i]>>t&1)) A[k].pb(i);
        for(int i=2;i<=n;i+=2)
            if(a[i]>>t&1) B[k].pb(i);
        if(A[k].empty() || B[k].empty())
            A[k].clear(),B[k].clear(),k--;
    }
    printf("%d\n",k);
    for(int i=1;i<=k;i++)
    {
        printf("%d %d",A[i].size(),B[i].size());
        for(int x:A[i]) printf(" %d",x);
        for(int x:B[i]) printf(" %d",x);
        puts("");
    }
}