test 190805 做实验

题目描述
有一天,你实验室的老板给你布置的这样一个实验。
首先他拿出了两个长度为 n 的数列 a 和 b,其中每个 $a_i$以二进制表示一个集
合。例如数字 $5 = (101)_2$ 表示集合 {1, 3}。第 i 次实验会准备一个小盒子,里面装
着集合 $a_i$ 所有非空子集的纸条。老板要求你从中摸出一张纸条,如果满足你摸出的
纸条是 $a_i$的子集而不是 $a_{i-b_i},a_{i-b_i+1}...a_{i-1}$ 任意一个的子集,那么你就要 ***;
反之,你就逃过一劫。
令你和老板都没有想到的是,你竟然每次都逃过一劫。在庆幸之余,为了知道
这件事发生的概率,你想要算出每次实验有多少纸条能使你 ***
输入格式
第一行一个数字 n。
接下来 n 行,每行两个整数,分别表示 a i 和 b i 。
输出格式
n 行,每行一个数字,表示第 i 次实验能使你 *** 的纸条数。
样例输入 1
3
7 0
15 1
3 1
样例输出 1
7
8
0
数据范围
对于 30% 的数据,n, a i , b i ≤ 100
对于 70% 的数据,n, a i , b i ≤ 60000
对于 100% 的数据,n, a i , b i ≤ 10 5
保证所有的 a i 不重复,b i < i

枚举某个k位二进制数的子集不需要$O(2^k)$

看如下代码

for(int j=a[i];j>0;j=((j-1)&a[i]))

这样便枚举了$a_i$的所有子集,复杂度为其个数

然后再用一个pos数组标记某个子集出现的最晚位置就好了

时间复杂度:$O(3^k),其中k=logn$

算法合理性证明:https://www.jianshu.com/p/cfe562dcf2f6

时间复杂度证明:https://www.cnblogs.com/MyNameIsPc/p/8876855.html

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n;
int a[N],b[N];
int pos[N];
int main()
{
    freopen("test.in","r",stdin); freopen("test.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%d%d",&a[i],&b[i]);
    for(int i=1;i<=n;++i)
    {
        int ans=0;
        for(int j=a[i];j>0;j=((j-1)&a[i]))
        {
            if(pos[j]<i-b[i]) ++ans;
            pos[j]=i;
        }
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/w19567/p/11302995.html
今日推荐