Problem A. Big Buttons The Ways Google Kickstart Round H 2018
阿新 • • 發佈:2018-12-22
題意:構造長度為N的字串,每一位可以是R或者B。給了P個字首,構造的字串不可包含這P個字首。(如果不是作為前綴出現則可以。)問有多少種構造方式。
如果字首長度是x,以此為字首,有2^(N-x)個字串是invalid的。
如果這P個字首是互斥的,invalid的總和就是 sum_x 2^(N-x),但是P個字首可能有重合,比如RB,RBBB這種。emmm然後我想到了容斥原理。但是容斥原理general case下要計算2^N個子集,而且也沒有利用到字首和的性質。
如果有兩個prefix一個是另一個的字首,比如N=4,R和RB,
R_ _ _ (1)
RB_ _ (2)
(1)中的RB_ _在(2)中也會出現所以(1)中Invalid字串只要考慮RR_ _ 。
對於兩個prefix A,B如果A是B的字首且len(B)-len(A)=y,A對應的invalid字串是(2^y-1)*(2^(N-len(A)-y)) ,重複的部分歸到B中計算。
有些坑:
1. prefix A可能是多個prefix的字首,例如 B,BR,BB
2. prefix A是prefix B的字首,prefix B是prefix C的字首,例如 B,BR,BRBB。A和C重合的部分已經被B排除了,所以只需要分別計算A and B, B and C。
#include<iostream> #include<stdio.h> #include<cstdio> #include<string> #include<cmath> #include<stdlib.h> #include<algorithm> #include<string.h> #include<cstring> #include<vector> #include<queue> #include<map> #include<set> #include<ctime> using namespace std; //Kickstart Round H Problem A const int maxn=110; int T; int P; int N; string arr[maxn]; long long ans; bool issubstr(int x,int y) { string tmp=arr[y].substr(0,arr[x].length()); return arr[x]==tmp; } long long twopow[maxn]; int mp[maxn][maxn]; int cmpans; char cmpstr[maxn]; void dfs(int n) { if(n==N) { bool flg=true; for(int i=0;i<P;i++) { bool equ=true; for(int j=0;j<arr[i].length();j++) { if(arr[i][j]!=cmpstr[j]) { equ=false; break; } } if(equ==true) { flg=false; } } if(flg==true) { cmpans++; } // cout<<cmpstr<<" "<<flg<<endl; return; } cmpstr[n]='R'; dfs(n+1); cmpstr[n]='B'; dfs(n+1); return; } int main() { // freopen("input.txt","r",stdin); freopen("A-large.in","r",stdin); freopen("Alarge.txt","w",stdout); // freopen("Asmall-cmp0.txt","w",stdout); clock_t START_TIME; clock_t FINISH_TIME; START_TIME=clock(); scanf("%d",&T); memset(twopow,0,sizeof(twopow)); twopow[0]=1; for(int i=1;i<maxn;i++) { twopow[i]=2*twopow[i-1]; } for(int ca=1;ca<=T;ca++) { memset(mp,0,sizeof(mp)); memset(cmpstr,0,sizeof(cmpstr)); ans=0; cmpans=0; cin>>N>>P; for(int i=0;i<P;i++) { string tmp; cin>>tmp; cin.ignore(); arr[i]=tmp; } sort(arr,arr+P); // for(int i=0;i<P;i++) // { // cout<<arr[i]<<" "; // } // cout<<endl; ans=twopow[N]; // cout<<ans<<endl; for(int i=0;i<P;i++) { for(int j=i+1;j<P;j++) { if(issubstr(i,j)==true) { mp[i][j]=1; } } } for(int i=0;i<P;i++) { for(int j=i+1;j<P;j++) { for(int k=i+1;k<j;k++) { if(mp[i][k]==1&&mp[k][j]==1) { mp[i][j]=0; break; } } } } // for(int i=0;i<P;i++) // { // for(int j=i+1;j<P;j++) // { // cout<<i<<" "<<j<<" "<<mp[i][j]<<endl; // } // } for(int i=0;i<P;i++) { long long tmp0=twopow[N-arr[i].length()]; // cout<<i<<" "<<tmp0<<endl; for(int j=i+1;j<P;j++) { if(mp[i][j]==1) { int len0=arr[j].length()-arr[i].length(); int len1=N-arr[j].length(); long long tmp=twopow[len1]; // cout<<i<<" "<<j<<" "<<len0<<" "<<len1<<" "<<tmp<<endl; tmp0-=tmp; } } // cout<<i<<" "<<tmp0<<endl; ans-=tmp0; } // dfs(0); // cout<<cmpans<<endl; // printf("Case #%d: %d\n",ca,cmpans); printf("Case #%d: %lld\n",ca,ans); cerr<<"finish case "<<ca<<endl; } FINISH_TIME=clock(); cerr<<1.0*(FINISH_TIME-START_TIME)/CLOCKS_PER_SEC <<" (s) "<<endl; return 0; }
其實更直觀的解法的把重複的部分算到字首上,對於prefix A, B並且A是B的字首,B對應的invalid字串已經被A對應的invalid字串包含了,所以把B刪除即可。==