zoukankan      html  css  js  c++  java
  • LOJ2494. 「AHOI / HNOI2018」寻宝游戏

    给出(n+1)个长度为(m)的二进制数组成的序列(规定(a_0=0),其它读入),每个询问给出一个二进制数。你需要求出在这个序列中每个相邻位置之间添加符号(and)(or)(规定运算从左往右进行),使得运算结果为询问的二进制数的方案数。

    (n,qle 1000,mle 5000)


    没有看到运算顺序所以想了一整个白天甚至浪费了一节物理课

    考虑运算加数字接在后面的意义:(and 0)(or 1)分别表示赋值,剩下两个分别表示保持不变。

    于是有个normal的模型转化:从后往前扫,维护个结果(res),一开始每个位置未确定,每次用(a_i)中的所有(0)或所有(1)覆盖(res)未确定的位置,要求覆盖的这个位置一定符合询问的数。可以直接暴力做,只要加上每个位置都被覆盖之后就退出的剪枝,就可以做到(O(frac{nmq}{omega}))的复杂度,出题人没有卡,可以过。(考虑没有确定的位置,假如选了(0),设(S_0)表示结果应当(0)的位置集合,(a_{i,0})表示(a_i)(0)的位置集合,如果(a_{i,0}subseteq S_0)才可以递归下去;选(1)同理。进而发现(a_{i,0}subseteq S_0 and a_{i,1}subseteq S_1Leftrightarrow (a_{i,0}=S_0)),也就是只有(a_{i,0}=S_0)时会分叉。注意到分叉之后,没有确定的位置的目标值只有一种(假设为(0)),如果选(1)那么只有影响到的位置是空集才合法,此时如果选(0)就是覆盖全集会被剪枝掉)

    正解很妙:

    考虑搞出一个操作序列。把(and)视作(1)(or)视作(0)

    (n)(m)位数转置变成(m)(n)位数。现在对于某个数(b_i),考虑它和(opt)的关系会如何决定结果。发现是从后往前,第一个不同的位置。假如这个位置上(opt)(b_i)分别为(0,1),那么最终结果是(1);如果是(1,0),或找不到不同的位置,结果是(0)

    这其实就是比大小,当(opt<b_i)时为(1),否则为(0)

    于是对(b_i)排序。每次根据询问的值计算出(opt)的范围即可。


    using namespace std;
    #include <bits/stdc++.h>
    #define N 1005
    #define M 5005
    #define ull unsigned long long
    #define mo 1000000007
    int n,m,Q;
    char str[M];
    int _b[M][N],*b[M],p[M],v[M];
    bool cmpb(int x[],int y[]){
    	for (int i=n;i>=1;--i){
    		if (x[i]<y[i]) return 1;
    		if (x[i]>y[i]) return 0;
    	}
    	return 0;
    }
    int main(){
    	freopen("in.txt","r",stdin);
    	scanf("%d%d%d",&n,&m,&Q);
    	for (int j=0;j<m;++j)
    		b[j]=_b[j];
    	for (int i=1;i<=n;++i){
    		scanf("%s",str);
    		for (int j=0;j<m;++j)
    			b[j][i]=str[j]-'0';
    	}
    	sort(b,b+m,cmpb);
    	for (int j=0;j<m;++j){
    		int re=(b[j]-_b[0])/N;
    		p[re]=j;
    		for (int i=n;i>=1;--i)
    			v[j]=(v[j]*2+b[j][i])%mo;
    	}
    	v[m+1]=1;
    	for (int i=1;i<=n;++i)
    		v[m+1]=v[m+1]*2%mo;
    	while (Q--){
    		scanf("%s",str);
    		int l=-1,r=m+1;
    		for (int j=0;j<m;++j)
    			if (str[j]=='1')
    				r=min(r,p[j]);
    			else
    				l=max(l,p[j]);
    		printf("%d
    ",l<r?(v[r]-(l>=0?v[l]:0)+mo)%mo:0);
    	}
    	return 0;
    }
    
  • 相关阅读:
    POJ 2337 【欧拉路径<包含输出>】.cpp
    Hlg 【表达式求值+欧拉路径】.cpp
    Hlg 1563 亲合数.cpp memset
    Hlg 1619 只有矩形.cpp【并查集】
    用unison来同步你的远程文件夹 Fwolf's Blog
    调试intellij IDEA hbase开发环境
    Bash: parsing arguments with ‘getopts’ | rsalveti's random thoughts
    十天内提高单词量到20000! (Vocabulary 10000)
    Unison File Synchronizer User Manual and Reference Guide
    Import errors in djangocms
  • 原文地址:https://www.cnblogs.com/jz-597/p/14284001.html
Copyright © 2011-2022 走看看