题面:https://codeforces.com/contest/1288/problem/D
题目大意:
给定n个序列,每个序列元素个数严格相等于m
你需要找到两个序列a[i]和a[j],使其每个对应位置的元素取大后得到b序列 b[k]=max(a[i][k],a[j][k])
且让b序列中的最小值最大
i可以等于j
解题思路:
二分假设这个b序列的最小值的值x
将a序列转化成01构成的二进制串存在数组b中
0表示当前位置的值<x
1表示当前位置的值>=x
每次便最多可以得到3e5个字符
双层循环i,j从0到255(最大)
只要满足b[i]存在且b[j]存在且b[i]和b[j]按位取或后得到的结果每一位都是1(即a[i]和a[j]这两个数列按照题目所述方式得到的最小值比当前枚举的x大,此时说明二分的x是可行的,返回true)
如果找不到,返回false,说明x二分得太大了
二分的过程中在返回true之前就可以记录一下当前枚举到的i和j
总时间复杂度最坏情况为为O(log1e9 * (3e5+255^2)) 约为O(1e7)满足题意
1 /* 2 Written By. StelaYuri 3 On 2020/01/14 4 */ 5 #include<bits/stdc++.h> 6 using namespace std; 7 int a[300050][10],b[300],n,m,ci,cj,cpd=0; 8 bool prim(int x){ 9 int i,j,d; 10 memset(b,0,sizeof b); 11 for(i=1;i<=n;i++){ 12 d=0; 13 for(j=0;j<m;j++) 14 if(a[i][j]>=x) 15 d|=(1<<j); 16 b[d]=i; 17 } 18 for(i=0;i<=cpd;i++) 19 for(j=0;j<=cpd;j++) 20 if(b[i]&&b[j]&&((i|j)==cpd)){ 21 ci=b[i]; 22 cj=b[j]; 23 return true; 24 } 25 return false; 26 } 27 int main(){ 28 ios::sync_with_stdio(0); 29 cin.tie(0);cout.tie(0); 30 int i,j,l=0,r=1e9,mid; 31 cin>>n>>m; 32 if(n==1){ 33 cout<<"1 1\n"; 34 return 0; 35 } 36 for(j=0;j<m;j++) 37 cpd|=(1<<j); 38 for(i=1;i<=n;i++) 39 for(j=0;j<m;j++) 40 cin>>a[i][j]; 41 while(l<=r){ 42 mid=(l+r)>>1; 43 if(prim(mid)) 44 l=mid+1; 45 else 46 r=mid-1; 47 } 48 prim(r);//保险起见再处理一次 49 cout<<ci<<‘ ‘<<cj<<endl; 50 51 return 0; 52 }
原文地址:https://www.cnblogs.com/stelayuri/p/12222939.html
时间: 2024-10-09 04:33:33