时间限制 \(2s\) | 空间限制 \(256M\)
\(Childan\) 正在编造一个传奇故事来销售他的赝品-一条带有强烈 \(Wu\) 意识的项链给 \(Kasouras\)。但是 \(Kasoura\) 先生不相信这个故事,所以他要问几个关于 \(Childan\) 所谓个人珍藏的项链的问题。
这个个人珍藏是一个多重集 \(??\) 的 \(??\) 个 \(01\) 字符串。
\(01\) 字符串是包含 \(??\) 个字符 \(“0”\) 和 \(“1”\) 的字符串。例如,如果 \(??=4\),则字符串 \(“0110”、“0000”\) 和 \(“1110”\) 是 \(01\) 字符串,但 \(“00110”\)(有 \(5\) 个字符,而不是 \(4\) 个)和 \(“0”\) 不是。
注意,多重集 \(??\) 可以包含相等的元素。
\(Kasoura\) 先生通常会提供一个 \(01\) 字符串 \(??\),并询问 \(Childan\) 在多重集 \(??\) 中有多少个字符串 \(s\) 对应的 \((??,??)\) 的 \(Wu\) 值不大于 \(??\)。
\(Kasoura\) 夫人和 \(Kasoura\) 先生认为,如果 \(??_??=??_??(1≤??≤??)\),则字符对的 \(Wu\) 值等于 \(??_??\),否则为 \(0\)。\(01\) 字符串对的 \(Wu\) 值是每个字符对的 \(Wu\) 值的总和。请注意,每个\(01\) 字符串的长度等于 \(??\)。
例如,如果 \(??=[4,5,3,6]\),\((“1001”、“1100”)\) 的 \(Wu\) 是 \(7\),因为这些字符串只在第一和第三位置上具有相等的字符,所以 \(??_1+??_3=4+3=7\)。
你需要帮助 \(Childan\) 回答 \(Kasoura\) 先生的问题。即在多重集 \(??\) 中找到对应的 \(Wu\) 值不大于 \(??\) 的字符串的数量。
我们首先观察到 \(n \le 12\),且题目中提到的字符串均为 \(01\) 串,可以考虑使用状态压缩。我们发现:对于长度为 \(12\) 的字符串,其可能的组合方案有 \(2^{12} = 4096\) 种,分别为从 \(0\) 到 \(4095\)。但是,题目中的 \(m\) 小于等于 \(5 * 10^5\),明显大于 \(4096\)。这意味着,给出的字符串中可能有重复的字符串出现,而我们在统计答案时只需要考虑某一个字符串出现的次数,所以我们可以考虑去重并记录个数。
但是我们仍然无法保证时间上的高效:依照当前做法,我们对于每次询问都要遍历一遍 \(0\) 到 \(4095\) 的所有 \(01\) 串并且遍历该字符串的长度以计算产生的价值,时间复杂度为 \(O(12 * 4096* q)\)。由于 \(1 \le q \le 5 * 10^5\),这样的算法仍然会超时。我们此时可以发现:\(q\) 也出现了大量重复的字符串。我们想到:如果我们直接从 \(0\) 到 \( 4095\) 预处理出所有可能的字符串,并计算这些字符串与给出的 \(m\) 个字符串匹配所得出的价值,那么我们可以直接找到每次询问对应的字符串与 \(m\) 个字符串匹配的结果。但是这样我们仍然需要在 \(O(4096 * q)\) 的时间复杂度内找出所有答案小于等于 \(k\) 的字符串,仍然无法通过此题。
我们研究性质,发现一个十分关键的性质:对于我们预处理出的字符串,在计算时其与 \(m\) 个字符串的答案只需要统计数量,与答案的顺序无关,因此我们可以将这些答案递增排序。剩下的做法就十分清晰了:我们通过二分找出最大的小于等于 \(k\) 的答案,计算从第一个字符串到当前答案中的字符串的个数的和。而对于这个和,我们可以提前预处理出来,使用前缀和在 \(O(1)\) 的时间复杂度内查询。这样一来,每次查询的时间复杂度就是 \(O(log_2 4096)\),也就是 \(O(12)\)。整个代码的时间复杂度就是 \(O(12*q)\),可以通过此题。
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 20;
const int M = 5000;
const int L = 5e5+10;
int n, m, q, x, x1, cnt, cnt2, cnt3, l, r, mid, ans, k, len;
int w[N], S[L], nu[M], ns[M];
struct node
{
int ans, ms, sum;
bool operator<(const node&x)const{return ans<x.ans;}
};
vector<node> v[M];
node y;
char s[N];
int main()
{
scanf("%d %d %d", &n, &m, &q);
for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
for (int i = 1; i <= m; i++)
{
x = 1;
scanf("%s", s);
len = strlen(s);
for (int j = len-1; j >= 0; j--)
{
if (s[j] == ‘1‘) S[i] += x;
x <<= 1;
}
}
S[0] = 9360;
sort(S+1, S+m+1);
for (int i = 1; i <= m; i++)
{
if (S[i] != S[i-1])
{
ns[++cnt] = S[i];
nu[cnt] = 1;
}
else nu[cnt]++;
}
for (int i = 0; i <= 4095; i++)
{
for (int j = 1; j <= cnt; j++)
{
cnt2 = 0;
cnt3 = 0;
x = i;
x1 = ns[j];
for (int k = 1; k <= n; k++)
{
if ((x & 1) == (x1 & 1)) cnt3 += w[n - cnt2];
cnt2++;
x >>= 1;
x1 >>= 1;
}
y.ans = cnt3;
y.ms = j;
v[i].push_back(y);
}
}
for (int i = 0; i <= 4095; i++) sort(v[i].begin(), v[i].end());
for (int i = 0; i <= 4095; i++)
{
for (int j = 0; j < v[i].size(); j++)
{
if (j) v[i][j].sum += v[i][j-1].sum;
v[i][j].sum += nu[v[i][j].ms];
}
}
for (int i = 1; i <= q; i++)
{
cnt2 = 0;
x = 1;
scanf("%s %d", s, &k);
len = strlen(s);
for (int j = len-1; j >= 0; j--)
{
if (s[j] == ‘1‘) cnt2 += x;
x <<= 1;
}
l = 0;
r = v[cnt2].size()-1;
while (l <= r)
{
mid = (l+r)/2;
if (v[cnt2][mid].ans <= k)
{
ans = mid;
l = mid+1;
}
else r = mid-1;
}
printf("%d\n", v[cnt2][ans].sum);
}
return 0;
}
原文:https://www.cnblogs.com/david24/p/14382358.html