2018"百度之星"程序設計大賽 - 資格賽 A/B/E/F


調查問卷

 
 Accepts: 505
 
 Submissions: 2436
 Time Limit: 6500/6000 MS (Java/Others)
 
 Memory Limit: 262144/262144 K (Java/Others)
Problem Description

度度熊為了完成畢業論文,需要收集一些數據來支撐他的論據,於是設計了一份包含 mm 個問題的調查問卷,每個問題只有 'A' 和 'B' 兩種選項。

將問卷散發出去之后,度度熊收到了 nn 份互不相同的問卷,在整理結果的時候,他發現可以只保留其中的一部分問題,使得這 nn 份問卷仍然是互不相同的。這里認為兩張問卷是不同的,當且僅當存在至少一個被保留的問題在這兩份問卷中的回答不同。

現在度度熊想知道,存在多少個問題集合,使得這 nn 份問卷在只保留這個集合的問題之后至少有 kk 對問卷是不同的。

Input

第一行包含一個整數 TT,表示有 TT 組測試數據。

接下來依次描述 TT 組測試數據。對於每組測試數據:

第一行包含三個整數 nn,mm 和 kk,含義同題目描述。

接下來 nn 行,每行包含一個長度為 mm 的只包含 'A' 和 'B' 的字符串,表示這份問卷對每個問題的回答。

保證 1 \leq T \leq 1001T100,1 \leq n \leq 10^31n103​​,1 \leq m \leq 101m10,1 \leq k \leq 10^61k106​​,給定的 nn 份問卷互不相同。

Output

對於每組測試數據,輸出一行信息 "Case #x: y"(不含引號),其中 x 表示這是第 xx 組測試數據,y 表示滿足條件的問題集合的個數,行末不要有多余空格。

Sample Input
2
2 2 1
AA
BB
2 2 2
AA
BB
Sample Output
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 }

 



子串查詢

 
 Accepts: 1706
 
 Submissions: 7443
 Time Limit: 3500/3000 MS (Java/Others)
 
 Memory Limit: 262144/262144 K (Java/Others)
 
           
Problem Description

度度熊的字符串課堂開始了!要以像度度熊一樣的天才為目標,努力奮斗哦!

為了檢驗你是否具備不聽課的資質,度度熊准備了一個只包含大寫英文字母的字符串 A[1,n] = a_1 a_2 \cdots a_nA[1,n]=a1​​a2​​an​​,接下來他會向你提出 qq 個問題 (l,r)(l,r),你需要回答字符串 A[l,r] = a_l a_{l+1} \cdots a_rA[l,r]=al​​al+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| = kS=k 且 |T| > kT>k,要么滿足 k < \min(|S|,|T|)k<min(S,T) 且 SS 的第 k+1k+1 個字符比 TT 的第 k+1k+1 個字符小。例如 "AA" 的字典序比 "AAA" 小,"AB" 的字典序比 "BA" 小。

Input

第一行包含一個整數 TT,表示有 TT 組測試數據。

接下來依次描述 TT 組測試數據。對於每組測試數據:

第一行包含兩個整數 nn 和 qq,表示字符串的長度以及詢問的次數。

第二行包含一個長為 nn 的只包含大寫英文字母的字符串 A[1,n]A[1,n]。

接下來 qq 行,每行包含兩個整數 l_i,r_ili​​,ri​​,表示第 ii 次詢問的參數。

保證 1 \leq T \leq 101T10,1 \leq n,q \leq 10^51n,q105​​,1 \leq l_i \leq r_i \leq n1li​​ri​​n。

Output

對於每組測試數據,先輸出一行信息 "Case #x:"(不含引號),其中 x 表示這是第 xx 組測試數據,接下來 qq 行,每行包含一個整數,表示字符串 A[l,r]A[l,r] 中字典序最小的子串個數,行末不要有多余空格。

Sample Input
1
2 3
AB
1 1
1 2
2 2
Sample Output
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 }

 

 
             


序列計數

 
 Accepts: 89
 
 Submissions: 566
 Time Limit: 4500/4000 MS (Java/Others)
 
 Memory Limit: 262144/262144 K (Java/Others)
Problem Description

度度熊了解到,11,22,…,nn 的排列一共有 n! = n \times (n-1) \times \cdots \times 1n!=n×(n1)××1 個。現在度度熊從所有排列中等概率隨機選出一個排列 p_1p1​​,p_2p2​​,…,p_npn​​,你需要對 kk=11,22,33,…,nn 分別求出長度為 kk的上升子序列個數,也就是計算滿足 1 \leq a_11a1​​ < a_2a2​​ < … < a_kak​​ \leq nn 且 p_{a_1}pa1​​​​ <p_{a_2}pa2​​​​< … < p_{a_k}pak​​​​ 的 kk 元組 (a_1a1​​,a_2a2​​,…,a_kak​​) 的個數。

由於結果可能很大,同時也是為了 ruin the legend, 你只需要輸出結果對 1000000007(=10^9+7)1000000007(=109​​+7) 取模后的值。

Input

第一行包含一個整數 TT,表示有 TT 組測試數據。

接下來依次描述 TT 組測試數據。對於每組測試數據:

第一行包含一個整數 nn,表示排列的長度。

第二行包含 nn 個整數 p_1p1​​,p_2p2​​, …, p_npn​​,表示排列的 nn 個數。

保證 1 \leq T \leq 1001T100,1 \leq n \leq 10^41n104​​,TT 組測試數據的 nn 之和 \leq 10^5105​​,p_1p1​​,p_2p2​​,…,p_npn​​ 是 11,22,…,nn 的一個排列。

除了樣例,你可以認為給定的排列是從所有 11,22,…,nn 的排列中等概率隨機選出的。

Output

對於每組測試數據,輸出一行信息 "Case #x: c_1c1​​ c_2c2​​ ... c_ncn​​"(不含引號),其中 x 表示這是第 xx 組測試數據,c_ici​​ 表示長度為 ii 的上升子序列個數對 1000000007(=10^9+7)1000000007(=109​​+7) 取模后的值,相鄰的兩個數中間用一個空格隔開,行末不要有多余空格。

Sample Input
2
4
1 2 3 4
4
1 3 2 4
 
 
 
 
 
   

    不難想到一個O(N*N*log(N))的做法,但是這個數據一直擔心會gg,寫了之后也確實T了。后來經過大佬指點發現,由於數據是隨機的,所以遞推到某一個長度之后可能沒有任何一種方案,那他之后更大的長度的方案個數也是0,就可以直接退出循環了。加一句話就過了。 
    f(i,j)表示以第i個數結尾的長度為j的上升序列的方案個數,轉移的時候用BIT維護下,要保證當前BIT內的數值都是當前位之前的位置貢獻的。

 
              
 
              
 1 #include<bits/stdc++.h>
 2 using namespace std;  3 #define LL long long 
 4 #define pb push_back
 5 #define inf 0x3f3f3f3f
 6 LL mod=1e9+7;  7 int a[10010],N,M;  8 LL C[10010],ans[10010],f[2][10010];  9 inline int lowbit(int x){return x&-x;}; 10 inline void add(int x,LL d){ 11     while(x<=N){ 12         C[x]+=d; 13         C[x]%=mod; 14         x+=lowbit(x); 15  } 16 } 17 inline LL sum(int x){ 18     LL r=0; 19     while(x){ 20         r+=C[x]; 21         r%=mod; 22         x-=lowbit(x); 23  } 24     return r; 25 } 26 inline void scan_d(int &ret) 27 { 28     char c; 29     ret = 0; 30     while ((c = getchar()) < '0' || c > '9'); 31     while (c >= '0' && c <= '9') 32  { 33         ret = ret * 10 + (c - '0'), c = getchar(); 34  } 35 } 36 int main(){ 37     int t,n,m,i,j,k,cas=0; 38     char ch; 39     scanf("%d",&t); 40     while(t--){ 41         scanf("%d",&n); 42         N=n; 43         for(i=1;i<=n;++i) scan_d(a[i])/*scanf("%d",a+i)*/,f[0][i]=1; 44         ans[1]=n; 45         int cur=1; 46         for(int len=2;len<=n;++len){ 47             ans[len]=0; 48             if(ans[len-1]==0) continue; 49             for(i=1;i<=n;++i){ 50                 LL tmp=sum(a[i]-1); 51                 ans[len]+=tmp; 52                 add(a[i],f[cur^1][i]); 53                 f[cur][i]=tmp; 54                 
55  } 56             //memset(C,0,sizeof(C));
57             for(i=1;i<=n;++i)C[i]=0; 58             cur^=1; 59  } 60         printf("Case #%d:",++cas); 61         for(i=1;i<=n;++i) printf(" %I64d",ans[i]%mod); 62             cout<<endl; 63  } 64     return 0; 65 }

 



三原色圖

 
 Accepts: 210
 
 Submissions: 865
 Time Limit: 1500/1000 MS (Java/Others)
 
 Memory Limit: 262144/262144 K (Java/Others)
 
              
Problem Description

度度熊有一張 nn 個點 mm 條邊的無向圖,所有點按照 1,2,\cdots,n1,2,,n 標號,每條邊有一個正整數權值以及一種色光三原色紅、綠、藍之一的顏色。

現在度度熊想選出恰好 kk 條邊,滿足只用這 kk 條邊之中的紅色邊和綠色邊就能使 nn 個點之間兩兩連通,或者只用這 kk 條邊之中的藍色邊和綠色邊就能使 nn 個點之間兩兩連通,這里兩個點連通是指從一個點出發沿着邊可以走到另一個點。

對於每個 k=1,2,\cdots,mk=1,2,,m,你都需要幫度度熊計算選出恰好 kk 條滿足條件的邊的權值之和的最小值。

Input

第一行包含一個正整數 TT,表示有 TT 組測試數據。

接下來依次描述 TT 組測試數據。對於每組測試數據:

第一行包含兩個整數 nn 和 mm,表示圖的點數和邊數。

接下來 mm 行,每行包含三個整數 a,b,wa,b,w 和一個字符 cc,表示有一條連接點 aa 與點 bb 的權值為 ww、顏色為 cc 的無向邊。

保證 1 \leq T \leq 1001T100,1 \leq n,m \leq 1001n,m100,1 \leq a,b \leq n1a,bn,1 \leq w \leq 10001w1000,c \in {R,G,B}c{R,G,B},這里 R,G,BR,G,B 分別表示紅色、綠色和藍色。

Output

對於每組測試數據,先輸出一行信息 "Case #x:"(不含引號),其中 x 表示這是第 xx 組測試數據,接下來 mm 行,每行包含一個整數,第 ii 行的整數表示選出恰好 ii 條滿足條件的邊的權值之和的最小值,如果不存在合法方案,輸出 -11,行末不要有多余空格。

Sample Input
1
5 8
1 5 1 R
2 1 2 R
5 4 5 R
4 5 3 G
1 3 3 G
4 3 5 G
5 4 1 B
1 2 2 B
Sample Output
Case #1:
-1
-1
-1
9
10
12
17
22  



    
跑兩次kruscal就好了,一次只用條件一的邊,一次用條件二的邊。每次跑完之后在依次加入剩下的邊更新ans,如果無法聯通答案就都是-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 int f[111];  7 int getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}  8 struct Edge{  9     int u,v,w,col,sel; 10     bool operator<(const Edge& C)const{ 11         return w<C.w; 12  } 13 }e[110]; 14 int ans[110],N,M; 15 void mst(int no){ 16     int cnt=0,cnw=0; 17     for(int i=1;i<=N;++i)f[i]=i; 18     for(int i=1;i<=M;++i)e[i].sel=0; 19     for(int i=1;cnt<N-1&&i<=M;++i){ 20         if(e[i].col==no) continue; 21         int fu=getf(e[i].u),fv=getf(e[i].v); 22         if(fu!=fv){ 23             e[i].sel=1; 24             cnt++; 25             cnw+=e[i].w; 26             f[fv]=fu; 27  } 28  } 29     if(cnt!=N-1) return; 30     if(ans[cnt]==-1) ans[cnt]=cnw; 31     else ans[cnt]=min(ans[cnt],cnw); 32     for(int i=1;i<=M;++i){ 33         if(e[i].sel) continue; 34         cnt++; 35         cnw+=e[i].w; 36         if(ans[cnt]==-1) ans[cnt]=cnw; 37         else ans[cnt]=min(ans[cnt],cnw); 38  } 39 } 40 int main(){ 41     int t,n,m,i,j,k,cas=0; 42     char ch; 43     scanf("%d",&t); 44     while(t--){ 45         scanf("%d%d",&n,&m); 46         N=n,M=m; 47         for(i=1;i<=m;++i){ 48             scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 49             //getchar();scanf("%c",&ch);
50             cin>>ch; 51             if(ch=='R') e[i].col=0; 52             else if(ch=='G') e[i].col=1; 53             else if(ch=='B') e[i].col=2; 54  } 55         sort(e+1,e+1+m); 56         memset(ans,-1,sizeof(ans)); 57         mst(0); 58         mst(2); 59         
60         printf("Case #%d:\n",++cas); 61         for(i=1;i<=m;++i) printf("%d\n",ans[i]); 62  } 63     return 0; 64 }
 
              

 

 
             


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM