\(\cal T_1\) 矩阵

Description

有一个 \(n\times n\) 的矩阵,初始时所有位置上都是零。每次操作你可以选择若干行和若干列,然后把这些行列交点处的位置都变成 \(1\).

现在,你需要在不超过 \(Q\) 次操作内,使得 \((i,j)\) 是零当且仅当 \(j\leqslant i\leqslant j+1\).

例如 \(n=5\) 时,你需要得到如下的矩阵:

01111
00111
10011
11001
11100

\(n\leqslant 3000,Q=26\).

Solution

构造一生之敌

首先可以简化这个问题,先考虑只有对角线为零的情况:发现对角线可以表示为 \(x=y\),于是枚举行列二进制数的每一位 \(k\),将 "行的第 \(k\) 位为零,列的第 \(k\) 位为 \(1\)","行的第 \(k\) 位为 \(1\),列的第 \(k\) 位为零" 的下标全置为 \(1\),那么最后只有对角线不会被置为 \(1\).

但是现在我们还要维护对角线下面一格。不妨将图形 "缩放" 一下,大概地 将上下两格缩成一格,不就又成一条对角线了嘛?观察得知,奇偶性相同的列的格子缩起来比较符合对角线,因为这样分类能恰好将两个零缩成一个格子,以奇数列为例,就是一二行、三四行……为一格。于是可以将 \(n\times n\) 的图形大概地分成两个 \(n/2\times n/2\) 的图形,总操作次数在 \(4\log n\) 级别,这是过不了的。

观察数据范围,我们发现 \(\text{C}(Q/2,6)>n/2\),这意味着如果用 \(1\) 的个数为 \(6\) 个的十三位二进制数给行列重标号是完全足够的!二进制 \(1\) 的个数相等的数字有什么用呢?这意味着,如果数字 \(x,y\) 不同,那么一定存在一个二进制位 \(k\),满足 \(x\) 的第 \(k\) 位为零,\(y\) 的第 \(k\) 位为 \(1\)。也就是说,我们不再用讨论行列的 \(0,1\) 情况了。于是操作数正好是 \(2\times 13=26\).

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 <vector>
# include <iostream>
# include <algorithm>
using namespace std;

const int maxn = 1505;

vector <int> vec[2];
int n,Ref[maxn],bit,N,M,Bit,_bit;
struct node { int x,y; } s[maxn];

inline void handleTask(int delta) {
    for(int b=0;b<13;++b) if((bit>>b&1) && !(Bit>>b&1)) {
        vec[0].clear(); vec[1].clear(); 
        for(int i=1;i<=N;++i) if(!(Ref[i]>>b&1)) {
            if(s[i].x) vec[0].emplace_back(s[i].x);
            if(s[i].y) vec[0].emplace_back(s[i].y);
        }
        if(delta) vec[0].emplace_back(1);
        sort(vec[0].begin(),vec[0].end());
        vec[0].erase(unique(vec[0].begin(),vec[0].end()),vec[0].end());
        for(int i=1;i<=M;++i) if(Ref[i]>>b&1)
            vec[1].emplace_back((i<<1)-1+delta);
        printf("%d %d",int(vec[0].size()),int(vec[1].size()));
        for(int d=0;d<2;++d) for(const auto& i:vec[d]) 
            putchar(' '), write(i); puts("");
    }
}

int main() {
    n=read(9), read(9); int idx=0; Bit=(1<<13)-1;
    for(int i=0, lim=1<<13; i<lim; ++i)
        if(__builtin_popcount(i)==6) {
            Ref[++idx]=i, bit|=i, Bit&=i; 
            if(idx==(n+1>>1)) break;
            else _bit = bit;
        }
    idx=0; for(int i=0;i<13;++i)
        if((bit>>i&1) && !(Bit>>i&1)) ++ idx;
    (n&1)? swap(bit,_bit): void(); for(int i=0;i<13;++i)
        if((bit>>i&1) && !(Bit>>i&1)) ++ idx;
    print(idx,'\n');
    for(int j=1;j<=n;j+=2) 
        s[j+1>>1] = (node){j,j+1<=n? j+1: 0};
    (n&1)? swap(bit,_bit): void();
    N=M=n+1>>1; handleTask(0);
    for(int j=2;j<n;j+=2)
        s[j>>1] = (node){j,j+1};
    if(n&1) s[N=(n+1>>1)] = (node){1,0}, M=n>>1;
    else s[N=(n>>1)] = (node){n,0}, M=n>>1;
    (n&1)? swap(bit,_bit): void(); handleTask(1);
    return 0;
}

\(\cal T_2\)

Description

Solution

Code


\(\cal T_3\)

Description

Solution

Code


posted on 2022-05-30 20:40  Oxide  阅读(65)  评论(0编辑  收藏  举报