codeforces 878D

思维题(看了题解后自己又画了半天才搞懂…)

题意:给出 k 12)个生物,每个生物有 n 105)个特性,然后 q 105)个操作,每次操作可以任选之前的两个生物产生一个新生物(特性为这两个生物的特性的max/min值),也可以询问之前某个生物的某个特性。

思路:每次产生的新生物的特性都是基于初始的 k 个生物,因此后续产生的每个生物都可以用前k个生物一系列的max/min操作来表示。这里我们给每个生物定义一个新属性( 2k 个比特位),以此来间接表示前 k 个生物的一系列操作后的结果。初始时,对于1~ k 中的第i生物而言,将 0 ~ 2k1这些数字中第 i 个比特为1的数字置为 1 ,其余为0。那么后续对于这些生物的max/min操作也就转换为了它们这个新属性的或/与运算。例如,操作max(第 i 生物,第j生物)得到的就是将 0 ~ 2k1这些数字中第 i 个比特为1或者第 j 个比特为1的数字置为 1 ,其余为0。(这时对于新生物而言, 0 ~ 2k1里面被置为1数字变多了);反之。那么询问生物 x 某个特性y操作时,我们依次在前 k 个生物从高到低枚举这个特性y(所对应的前 k 个生物的编号),然后看看当前枚举到的生物的新属性是否被生物x的新属性包括在内(也就是看 x 的新属性中的某个比特位是否为1)。

#include <cstdio>
#include <algorithm>
#include <bitset>

using namespace std;
const int maxn = 100050;

int a[15][maxn], rk[maxn][15];
bitset<4096> bt[maxn];

int cmp_id;
bool cmp(const int x, const int y) {
    return a[x][cmp_id] < a[y][cmp_id];
}

int main() {
    int n, k, q;
    while(scanf("%d%d%d",&n,&k,&q) == 3) {
        for(int i=1; i<=k; i++)
            for(int j=1; j<=n; j++) {
                scanf("%d",&a[i][j]);
                rk[j][i] = i;
            }
        for(int j=1; j<=n; j++) {
            cmp_id = j;
            sort(rk[j]+1, rk[j]+k+1, cmp);
        }
        for(int i=1; i<=k; i++)
            for(int j=0; j<(1<<k); j++) {
                if(j&(1<<(i-1))) bt[i][j] = 1;
                else bt[i][j] = 0;
            }
        int tot = k;
        while(q --) {
            int op, x, y;
            scanf("%d%d%d",&op,&x,&y);
            if(op == 1)
                bt[++tot] = bt[x] | bt[y];
            else if(op == 2)
                bt[++tot] = bt[x] & bt[y];
            else {
                int id = k+1, cur = 0;
                while(bt[x][cur] == 0) {
                    id --;
                    cur |= 1<<(rk[y][id]-1);
                }
                printf("%d\n",a[rk[y][id]][y]);
            }
            /*printf("tot:%d\n",tot);
            for(int j=0; j<(1<<k); j++)
                printf("%d ",(int)bt[tot][j]);
            puts("");*/
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值