調查問卷
度度熊為了完成畢業論文,需要收集一些數據來支撐他的論據,於是設計了一份包含 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