题解 P6567 【[NOI Online #3 入门组]买表】
考场上的错误:
-
输出大小写
-
二进制优化写挂/kk
这个题目就是变形的多重背包,那么不难想到,用一个
但是这样极限数据是
接下来就是套路性的优化多重背包了:二进制优化。
比如有
那么能不能构造另一些物品,让这些物品同样能组合出
可以的。
我们需要构造的是价值为
用二进制的位值原理法可以得出这个是正确的。首先,除最后的一个物品可以组合出 价值
好处是物品数量由 虽然在洛谷的脚造数据可以过
那么还有一个好东西:bitset
上面的 dp 看起来是这个样子的:
for(j=5e5-l*k;j>=0;j--)
if(dp[j])
dp[j+l*k]=1;
欸,那不就是位运算吗?说清楚一些,如果把 dp 数组当作一个大 bool 类型,那么这个操作就等同于 dp|=dp<<l*k。
正好,万能的 STL 库中就有这个好东西 bitset,本质上是一个 bool 数组(内置是用 ull 压了位),但是仍然支持各种位运算。
这样定义:
bitset<500005> dp;
操作也很简洁:
dp|=dp<<l*k;
常数是一般的 bool 数组的
然后凭借卡常技巧卡到了 rank 1/cy
#include<bits/stdc++.h>
using namespace std;
bitset<500005> dp;
int main()
{
// freopen("watch.in","r",stdin);
// freopen("watch.out","w",stdout);
int n,m,i,j,a,k,l;
scanf("%d%d",&n,&m);
dp[0]=1;
for(i=0;i<n;i++)
{
scanf("%d%d",&k,&a);
for(l=1;a>=l;l*=2)
{
dp|=dp<<l*k;
a-=l;
}
if(a*k)
dp|=dp<<a*k;
}
while(m--)
{
scanf("%d",&k);
puts(dp[k]?"Yes":"No");
}
return 0;
}