2 条题解

  • 0
    @ 2026-8-5 22:44:11
    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e4+5,B=300,M=65;
    int n,q,a[N],op,x,y,vs,*v,*w;
    int be[N],en[B],si[B],_n,ans;
    struct no{
    	int o[B+5],w[M],v[M],w2[M],v2[M],vs,vs2,all;
    	void build(int l,int r){
    		for(int i=1;i<=r-l+1;++i)o[i]=0;all=vs=vs2=0;
    		for(int i=l;i<=r;++i){
    			for(int j=1;j<=vs;++j)v[j]|=a[i];
    			v[++vs]=a[i];w[vs]=i;all|=a[i];
    			if(v2[vs2]<all)v2[++vs2]=all,w2[vs2]=i;
    			int k=vs;vs=0;
    			for(int j=1;j<=k;++j){
    				if(v[j]!=v[vs])v[++vs]=v[j];w[vs]=w[j];
    				o[i-w[j]+1]=max(o[i-w[j]+1],v[j]);
    			}
    		}
    		reverse(v+1,v+vs+1);reverse(w+1,w+vs+1);
    		for(int i=1;i<=r-l+1;++i)o[i]=max(o[i],o[i-1]);
    	}
    }t[B];
    
    int main(){
    	scanf("%d%d",&n,&q);
    	for(int i=1;i<=n;++i)scanf("%d",&a[i]),be[i]=i/B+1,en[be[i]]=i;
    	_n=be[n];
    	for(int i=1;i<=_n;++i)t[i].build(en[i-1]+1,en[i]),si[i]=en[i]-en[i-1];
    	for(;q--;){
    		scanf("%d%d",&op,&x);
    		if(op==1){
    			scanf("%d",&y);a[x]=y;t[be[x]].build(en[be[x]-1]+1,en[be[x]]);
    		}else{
    			ans=n+1;
    			for(int i=1;i<=_n;++i)for(int j=min(ans,si[i]);j>=1;--j)if(t[i].o[j]>=x)ans=j;else break;
    			vs=0;
    			for(int i=1;i<=_n;++i){
    				for(int j=1;j<=t[i].vs2;++j){
    					y=t[i].v2[j];
    					for(;vs&&t[i].w2[j]-w[vs]+1>=ans;)--vs;
    					if(!vs)break;
    					if((y|v[vs])>=x){
    						for(;vs&&(y|v[vs])>=x;)--vs;
    						ans=t[i].w2[j]-w[vs+1]+1;
    					}
    				}
    				int V=vs,*v2=v,*w2=w;y=t[i].all;
    				v=t[i].v;w=t[i].w;vs=t[i].vs;
    				for(int i=1;i<=V;++i)if((y|v2[i])!=y)v[++vs]=y|=v2[i],w[vs]=w2[i];
    			}
    			printf("%d\n", ans==n+1?-1:ans);
    		}
    	}
    }
    
    • 0
      @ 2026-8-5 22:32:25
      #include <bits/stdc++.h>//55分代码
      
      #define int long long
      using namespace std;
      
      const int N = 5e4 + 10, sqrtN = 250;
      // 单点修改,查询区间 or 和 >= k 的最短长度
      // a[i]记录每个点的值,b[i]记录每个点i所在块;
      // block_or[i]记录第i个块的or和,total_or记录整个序列的or和
      // 每个块i的左端点L[i]、右端点R[i]
      int n, q, a[N], b[N], L[sqrtN], R[sqrtN], block_or[sqrtN], total_or;
      
      // 分块查询区间 [l, r] 的 or 和
      int query_or(int l, int r) {
          int res = 0;
          if (b[l] == b[r]) {
              for (int i = l; i <= r; i++) res |= a[i];
          } else {
              for (int i = l; i <= R[b[l]]; i++) res |= a[i];
              for (int i = b[l] + 1; i <= b[r] - 1; i++) res |= block_or[i];
              for (int i = L[b[r]]; i <= r; i++) res |= a[i];
          }
          return res;
      }
      
      signed main() {
          ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
          cin >> n >> q;
          for (int i = 1; i <= n; i++) {
              cin >> a[i];
          }
          int B = sqrt(n), cnt = (n + B - 1) / B;
          for (int i = 1; i <= n; i++) {
              b[i] = (i - 1) / B + 1;
          }
          for (int i = 1; i <= cnt; i++) {
              L[i] = (i - 1) * B + 1;
              R[i] = min(i * B, n);
          }
          
          total_or = 0;
          for (int i = 1; i <= cnt; i++) {
              block_or[i] = 0;
              for (int j = L[i]; j <= R[i]; j++) {
                  block_or[i] |= a[j];
              }
              total_or |= block_or[i];
          }
          
          for (int i = 1; i <= q; i++) {
              int op;
              cin >> op;
              if (op == 1) {
                  int pos, x;
                  cin >> pos >> x;
                  int blk = b[pos];
                  a[pos] = x;
                  
                  // 修正:必须重新计算,因为 |= 无法处理某一位从 1 变成 0 的情况
                  block_or[blk] = 0;
                  for (int j = L[blk]; j <= R[blk]; j++) {
                      block_or[blk] |= a[j];
                  }
                  total_or = 0;
                  for (int j = 1; j <= cnt; j++) {
                      total_or |= block_or[j];
                  }
              } else {
                  int k;
                  cin >> k;
                  if (total_or < k) {
                      cout << -1 << '\n';
                      continue;
                  }
                  
                  int ans = n + 1; // 初始化为不可能达到的最大值
                  for (int l = 1; l <= n; l++) {
                      // 剪枝:如果从 l 到 n 的 or 和都小于 k,那么后面的 l 区间更短,更不可能满足,直接 break
                      if (query_or(l, n) < k) break;
                      
                      // 二分查找不断缩小 limit (即右端点 r) 的范围,直到找到刚好 >= k 的最小右端点
                      // 因为我们只关心比当前已知最优解 ans 更短的长度,所以右端点上限为 l + ans - 1
                      int low = l, high = min(n, l + ans - 1);
                      int best_r = n + 1;
                      
                      while (low <= high) {
                          int mid = (low + high) / 2;
                          if (query_or(l, mid) >= k) {
                              best_r = mid;
                              high = mid - 1; // 满足条件,尝试缩小右端点以寻找更短的区间
                          } else {
                              low = mid + 1;  // 不满足条件,必须扩大右端点
                          }
                      }
                      
                      if (best_r <= n) {
                          ans = min(ans, best_r - l + 1);
                      }
                  }
                  cout << (ans == n + 1 ? -1 : ans) << '\n';
              }
          }
          return 0;
      }
      
      • 1

      信息

      ID
      11343
      时间
      2000ms
      内存
      256MiB
      难度
      10
      标签
      递交数
      12
      已通过
      1
      上传者