不妨考虑一种特殊情况,权值为 \(0/1\) 如何求解?

此时 \(k\) 个数可以表示为 \(n\) 位二进制数,

注意到位是独立的,将每一位拆开后最多只会有 \(\min(2^k,n)\) 种不同的情况。

\(2^k < n\) , 那么我们可以忽略列数而关心列的状态

那么记 \(f_{i,S}\) 表示第 \(i\) 个元素,前 \(k\) 个元素构成的列的状态为 \(S\) 时该位的值。

那么,\(\max\) 操作相当于或, \(\min\) 操作相当于与

对于一般情况,注意到答案只会是原来的 \(k\) 个元素的权值之一,枚举这个权值 \(v\) ,然后 \(\ge v\) 的位置视为 \(1\) , \(< v\) 的位置视为 \(0\),就转化为 \(0/1\) 的情况。

可以通过 bitset 优化做到 \(\mathcal O(2^k(k+\frac{q}{\omega}))\)

#include <bits/stdc++.h>
using namespace std;
#define pii pair< int , int >
#define fi first
#define sc second
#define mp make_pair

const int MAXN = 1e5 , MAXK = 12;
int n , k , q , a[ MAXK + 5 ][ MAXN + 5 ];
vector< pii > val[ MAXN + 5 ];
bitset< 1 << MAXK > f[ MAXN + 20 ];

int main( ) {
    scanf("%d %d %d",&n,&k,&q);
    for( int i = 1 ; i <= k ; i ++ ) {
        for( int j = 1 ; j <= n ; j ++ ) 
            scanf("%d",&a[ i ][ j ]) , val[ j ].push_back( mp( a[ i ][ j ] , i - 1 ) );
        for( int S = 0 ; S < 1 << k ; S ++ ) f[ i ][ S ] = ( S >> i - 1 ) & 1;
    }
    for( int j = 1 ; j <= n ; j ++ ) sort( val[ j ].begin() , val[ j ].end() );

    int cnt = k;
    for( int i = 1 , t , x , y ; i <= q ; i ++ ) {
        scanf("%d %d %d",&t,&x,&y);
        if( t == 1 ) f[ ++ cnt ] = f[ x ] | f[ y ];
        if( t == 2 ) f[ ++ cnt ] = f[ x ] & f[ y ];
        if( t == 3 ) {
            int ans = 0;
            for( int j = k - 1 , cur = 0 ; j >= 0 ; j -- ) {
                cur |= 1 << val[ y ][ j ].sc;
                if( f[ x ][ cur ] ) { ans = val[ y ][ j ].fi; break; }                         
            }
            printf("%d\n", ans );
        }
    }
    return 0;
}

原文地址:http://www.cnblogs.com/chihik/p/CF878D.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性