调查问卷
度度熊为了完成毕业论文,需要收集一些数据来支撑他的论据,于是设计了一份包含 mm 个问题的调查问卷,每个问题只有 'A' 和 'B' 两种选项。
将问卷散发出去之后,度度熊收到了 nn 份互不相同的问卷,在整理结果的时候,他发现可以只保留其中的一部分问题,使得这 nn 份问卷仍然是互不相同的。这里认为两张问卷是不同的,当且仅当存在至少一个被保留的问题在这两份问卷中的回答不同。
现在度度熊想知道,存在多少个问题集合,使得这 nn 份问卷在只保留这个集合的问题之后至少有 kk 对问卷是不同的。
第一行包含一个整数 TT,表示有 TT 组测试数据。
接下来依次描述 TT 组测试数据。对于每组测试数据:
第一行包含三个整数 nn,mm 和 kk,含义同题目描述。
接下来 nn 行,每行包含一个长度为 mm 的只包含 'A' 和 'B' 的字符串,表示这份问卷对每个问题的回答。
保证 1 \leq T \leq 1001≤T≤100,1 \leq n \leq 10^31≤n≤103,1 \leq m \leq 101≤m≤10,1 \leq k \leq 10^61≤k≤106,给定的 nn 份问卷互不相同。
对于每组测试数据,输出一行信息 "Case #x: y"(不含引号),其中 x 表示这是第 xx 组测试数据,y 表示满足条件的问题集合的个数,行末不要有多余空格。
2
2 2 1
AA
BB
2 2 2
AA
BB
Case #1: 3
Case #2: 0
注意到m很小,可以暴力枚举所有的子集然后判断不同的对数是否大于等于k。注意可能有多个人的问卷是一样的。
1 #include<bits/stdc++.h>
2 using namespace std;
3 #define LL long long
4 #define pb push_back
5 #define inf 0x3f3f3f3f
6 int vis[1111];
7 char e[1111][20];
8 int main(){
9 int t,n,q,m,i,j,cas=0,k;
10 scanf("%d",&t);
11 while(t--){
12 scanf("%d%d%d",&n,&m,&k);
13 for(i=0;i<n;++i) scanf("%s",e[i]);
14 int ans=0,all=((1<<m)-1);
15 for(int i=0;i<=all;++i){
16 int x=0,y=0;
17 memset(vis,0,sizeof(vis));
18 for(int j=0;j<n;++j){
19 x=0;
20 for(int k=0;k<m;++k){
21 if(i&(1<<k)){
22 x<<=1;
23 if(e[j][k]=='B') x|=1;
24 }
25 }
26 vis[x]++;
27 }
28 LL ss=0,tmp=0;
29 for(int j=0;ss<n;++j){
30 if(!vis[j]) continue;
31 tmp+=1LL*vis[j]*(n-ss-vis[j]);
32 ss+=vis[j];
33 }
34 if(tmp>=k)ans++;
35 }
36 printf("Case #%d: %d\n",++cas,ans);
37 }
38 return 0;
39 }
子串查询
度度熊的字符串课堂开始了!要以像度度熊一样的天才为目标,努力奋斗哦!
为了检验你是否具备不听课的资质,度度熊准备了一个只包含大写英文字母的字符串 A[1,n] = a_1 a_2 \cdots a_nA[1,n]=a1a2⋯an,接下来他会向你提出 qq 个问题 (l,r)(l,r),你需要回答字符串 A[l,r] = a_l a_{l+1} \cdots a_rA[l,r]=alal+1⋯ar 内有多少个非空子串是 A[l,r]A[l,r] 的所有非空子串中字典序最小的。这里的非空子串是字符串中由至少一个位置连续的字符组成的子序列,两个子串是不同的当且仅当这两个子串内容不完全相同或者出现在不同的位置。
记 |S|∣S∣ 为字符串 SS 的长度,对于两个字符串 SS 和 TT ,定义 SS 的字典序比 TT 小,当且仅当存在非负整数 k(\leq \min(|S|,|T|))k(≤min(∣S∣,∣T∣)) 使得 SS 的前 kk 个字符与 TT 的前 kk 个字符对应相同,并且要么满足 |S| = k∣S∣=k 且 |T| > k∣T∣>k,要么满足 k < \min(|S|,|T|)k<min(∣S∣,∣T∣) 且 SS 的第 k+1k+1 个字符比 TT 的第 k+1k+1 个字符小。例如 "AA" 的字典序比 "AAA" 小,"AB" 的字典序比 "BA" 小。
第一行包含一个整数 TT,表示有 TT 组测试数据。
接下来依次描述 TT 组测试数据。对于每组测试数据:
第一行包含两个整数 nn 和 qq,表示字符串的长度以及询问的次数。
第二行包含一个长为 nn 的只包含大写英文字母的字符串 A[1,n]A[1,n]。
接下来 qq 行,每行包含两个整数 l_i,r_ili,ri,表示第 ii 次询问的参数。
保证 1 \leq T \leq 101≤T≤10,1 \leq n,q \leq 10^51≤n,q≤105,1 \leq l_i \leq r_i \leq n1≤li≤ri≤n。
对于每组测试数据,先输出一行信息 "Case #x:"(不含引号),其中 x 表示这是第 xx 组测试数据,接下来 qq 行,每行包含一个整数,表示字符串 A[l,r]A[l,r] 中字典序最小的子串个数,行末不要有多余空格。
1
2 3
AB
1 1
1 2
2 2
Case #1:
1
1
1
字典序最小的子串,显然长度是1,做个前缀和统计一下不同字母出现的次数就好了。然后找区间内最小的且出现次数不为零的字母就是答案。
1 #include<bits/stdc++.h>
2 using namespace std;
3 #define LL long long
4 #define pb push_back
5 #define inf 0x3f3f3f3f
6 char s[100010];
7 LL f[100010][26];
8 int main(){
9 int t,n,q,m,i,j,k,cas=0,l,r;
10 scanf("%d",&t);
11 while(t--){
12 scanf("%d%d%s",&n,&q,s+1);
13 for(i=1;i<=n;++i){
14 for(j=0;j<26;++j) f[i][j]=f[i-1][j];
15 f[i][s[i]-'A']++;
16 }
17 printf("Case #%d:\n",++cas);
18 while(q--){
19 scanf("%d%d",&l,&r);
20 LL ans=0;
21 for(i=0;i<26;++i){
22 if(f[r][i]-f[l-1][i]){
23 ans=f[r][i]-f[l-1][i];
24 break;
25 }
26 }
27 printf("%I64d\n",ans);
28 }
29 }
30 return 0;
31 }
序列计数
度度熊了解到,11,22,…,nn 的排列一共有 n! = n \times (n-1) \times \cdots \times 1n!=n×(n−1)×⋯×1 个。现在度度熊从所有排列中等概率随机选出一个排列 p_1p1,p_2p2,…,p_npn,你需要对 kk=11,22,33,…,nn 分别求出长度为 kk的上升子序列个数,也就是计算满足 1 \leq a_11≤a1 < a_2a2 < … < a_kak \leq n≤n 且 p_{a_1}pa1 <p_{a_2}pa2< … < p_{a_k}pak