CF1017D Solution
作者:互联网
题解
\(n\)的数字范围很小,又加之01串的条件,容易想到状压。01串的个数很少,一共只有\(2^{12}=4096\)个,而其余都是重复的,并且\(w,k\le 100\),可以想到将每个字符串在不同\(k\)时的答案预处理。具体方法受Trie树的启发,想到将集合\(S\)中的01串以二叉树存储,每次递归计算可行数量,最后前缀和思想累加即可。
AC题解
#include<bits/stdc++.h>
using namespace std;
const int N=15,M=5e5+10,S=(1<<12)+10;
struct node {char str[N]; int v,k,id;} a[M];
//a:询问——str:原01串,v:状压值,id:原下标
char s[M][N];
int w[N],t[4*S][2],num[4*S],qwq[S][N*110],cnt,n;
//t:类Trie的二叉树,num:叶子节点01串数量,qwq[i][j]:状压值为i,k为j询问的答案,cnt:树中节点数量
bool cmp(node a,node b) {return a.v<b.v;}
bool cmp2(node a,node b) {return a.id<b.id;}
void add(char *b)//将新01串加入树中
{
int pos=0;
for(int i=1;i<=n;i++)
{
bool ti=b[i]-'0';
if(!t[pos][ti]) t[pos][ti]=++cnt;
pos=t[pos][ti];
if(i==n) num[pos]++;
}
}
void cul(node b,int x,int pos,int sum)//计算每个询问中01串的qwq
//x:树中下标,pos:串中下标,sum:Wu值总和
{
bool tmp=b.str[pos]-'0';
if(pos==n) {qwq[b.v][sum]+=num[x]; return;}
if(t[x][tmp]) cul(b,t[x][tmp],pos+1,sum+w[pos]);
if(t[x][tmp^1]) cul(b,t[x][tmp^1],pos+1,sum);
}
int main()
{
int m,q;
scanf("%d%d%d",&n,&m,&q);
for(int i=0;i<n;i++) scanf("%d",&w[i]);
for(int i=1;i<=m;i++) {scanf("%s",s[i]+1); add(s[i]);}
for(int i=1;i<=q;i++)
{
scanf("%s%d",a[i].str,&a[i].k); a[i].id=i;
for(int j=0;j<n;j++) a[i].v+=(a[i].str[j]-'0')*(1<<j);//二进制转十进制(求状压值)
}
sort(a+1,a+q+1,cmp); a[0].v=-1;//使v相同询问在一起
for(int i=1;i<=q;i++)
{
if(a[i].v==a[i-1].v) continue;
cul(a[i],0,0,0);
}
for(int i=1;i<=q;i++)
{
if(a[i].v!=a[i-1].v)//>=j的询问均可以使用j的答案,因此需要累加
for(int j=1;j<=100;j++) qwq[a[i].v][j]+=qwq[a[i].v][j-1];
}
sort(a+1,a+q+1,cmp2);//按原序排序,以便输出
for(int i=1;i<=q;i++) printf("%d\n",qwq[a[i].v][a[i].k]);
return 0;
}
标签:01,int,题解,想到,Solution,累加,CF1017D,答案 来源: https://www.cnblogs.com/violetholmes/p/14336466.html