校内资格赛题目,差点高一就要\(\tt{AFO}\)了
30分思路
对30%的数据,满足\(1≤n≤10\)
所以我们可以子集枚举,实际得分40pts
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int read(){ int k=0; char c=getchar(); for(;c<'0'||c>'9';) c=getchar(); for(;c>='0'&&c<='9';c=getchar()) k=k*10+c-48; return k; } char word[51][110]; bool sa[51][51]; long long ans; int main(){ int n=read(); for(int i=1;i<=n;i++) scanf("%s",word[i]); for(int i=1;i<=n;i++) for(int j=i;j<=n;j++){ bool flag=0; int leni=strlen(word[i]),lenj=strlen(word[j]); for(int pos=0;pos<=min(leni,lenj)-1;pos++){ if(word[i][pos]!=word[j][pos]){ flag=1; break; } } if(!flag) sa[i][j]=sa[j][i]=1; } for(int i=1;i<=(1<<n)-1;i++){ //很奇怪的子集枚举 int s[1010],tot=0,top=0,x=i; while(x){ if(x&1) s[++top]=tot; x>>=1; tot++; } bool flag=0; for(int i=1;i<=top;i++){ for(int j=i+1;j<=top;j++){ if(sa[s[i]][s[j]]){ flag=1; break; } } if(flag) break; } if(!flag) ans++; } cout<<ans+1; return 0; }
正解
我们先对单词排序,然后预处理出每两个之间是否是安全的,令dp[i]
表示前\(i\)个单词中包含第\(i\)个单词的子集个数,如果\(j<i\)并且他们是安全的,dp[i]+=dp[j]
。统计答案的时候,将\(dp[i]\)累加起来就好了
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int read(){ int k=0; char c=getchar(); for(;c<'0'||c>'9';) c=getchar(); for(;c>='0'&&c<='9';c=getchar()) k=k*10+c-48; return k; } string word[51]; bool sa[51][51]; long long ans,dp[110]; int main(){ int n=read(); for(int i=1;i<=n;i++) cin>>word[i],dp[i]=1; sort(word+1,word+n+1); for(int i=1;i<=n;i++) for(int j=i;j<=n;j++){ bool flag=0; int leni=word[i].size(),lenj=word[j].size(); for(int pos=0;pos<=min(leni,lenj)-1;pos++){ if(word[i][pos]!=word[j][pos]){ flag=1; break; } } if(!flag) sa[i][j]=sa[j][i]=1; } for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) dp[j]+=sa[i][j]? 0 : dp[i]; for(int i=1;i<=n;i++) ans+=dp[i]; cout<<ans+1; return 0; }