3 条题解
-
3
注意到n<=18,我将用最直白,最易懂,最不绕弯子的方式告诉你,看到这个范围十有八九是状态压缩的题。
根据乘法原理,单个字符串的贡献是(因为有个位置可以填,每个位置都有种选择),但是我们并不能简单地把贡献加起来,因为可能有2个字符串都能产生相同的答案,比如:
注意到或者会在这两个字符串的贡献中,因此需要减去重复的这一部分。
重复的部分有多少呢?如果要同时出现在两个串的贡献中,他的字符必须是两个字符串所共有的,也就是交集。
重复这个操作,观察规律(或者灵光一现)不难发现,在每一个n个字符串的子集中,若包含偶数个字符串,则减去贡献,否则加上贡献,据此状压并计算即可。
戴马:
#include<bits/stdc++.h> using namespace std; #define int long long const int N=18,M=30,mod=998244353; char s[N][M]; int qpow(int a,int b){ int res=1; for(;b;b/=2,a=a*a%mod)if(b&1)res=res*a%mod; return res; } signed main(){ int n,l;scanf("%lld%lld",&n,&l); for(int i=1;i<=n;i++)scanf("%s",s[i]+1); int ans=0; for(int i=1;i<(1<<n);i++){ int v[M];memset(v,0,sizeof(v)); int sum=0,jj=0; for(int j=1;j<=n;j++)if(i&(1<<(j-1))){ sum++;for(int k=1;k<=strlen(s[j]+1);k++)v[s[j][k]-'a'+1]++; } for(int j=1;j<=26;j++)if(v[j]==sum)jj++; if(sum%2==1)ans+=qpow(jj,l),ans%=mod; else ans-=qpow(jj,l),ans%=mod,ans+=mod,ans%=mod; } printf("%lld\n",ans); return 0; }我觉得你们应该看得懂。
-
2
又一次注意到,每日随便造。
思路
不难发现,如果只有一层,很明显答案数就是可以使用的字母种类数的次方。
但是如果有多层,答案就会有重复,考虑使用容斥去重。鉴于,不妨枚举那些层的键盘拥有一样的建,再将这些键可以造成的贡献容斥一下,最后统计答案即可。
(此题可以用bitset优化,但是本蒟蒻并没有用,可以看看那些巨佬可以优化一下)
AC代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N=30,P=998244353; int qpow(int a,int b) { int res=1; for(;b;b>>=1,a=a*a%P)if(b&1)res=res*a%P; return res; } int getcnt(int a)//统计a中有多少个二进制位是1 { int cnt=0; while(a) { cnt++; a-=a&-a; } return cnt; } int calcid(int x)//计算他有几位(相当于log) { int cnt=0; while(x)cnt++,x>>=1; return cnt; } char s[N]; int a[N],n,l,c[N][N]; signed main() { scanf("%lld%lld",&n,&l); for(int i=1;i<=n;i++) { scanf("%s",s+1); int len=strlen(s+1); for(int j=1;j<=len;j++)a[i]|=(1<<s[j]-'a');//状态压缩一下有那些键 } int ans=0; for(int i=1;i<(1<<n);i++)//枚举每种状态 { int xxx=i,x=(1<<26)-1; while(xxx) { int pos=xxx&-xxx;//必须是共同的按键,所以是& x&=a[calcid(pos)]; xxx-=xxx&-xxx; } int f,cntx=getcnt(x); if(getcnt(i)&1)f=1;//奇数个 else f=-1;//偶数个 ans=((ans+f*qpow(cntx,l)%P)%P+P)%P; } printf("%lld\n",ans); return 0;//完结撒花~ } -
2
在单独指定字符集与长度时答案是很好算的,但在这里有可能会使能同时被两个字符集表示出来的字符串(显然就是能被两个字符集的并集表示出来的字符串),但减去后又会少考虑能同时被三个字符集表示出来的字符串...
发现这和容斥原理非常像,同时注意到 特别小,所以考虑容斥。
#include<bits/stdc++.h> using namespace std; bitset<30> bit[20]; const int mod=998244353; int count(int x){ int ji=0; while(x){ ji++; x=x&(x-1); } return ji; } long long power(long long a,long long b){ long long ans=1; while(b){ if(b&1)ans=ans*a%mod; a=a*a%mod; b>>=1; } return ans; } int main(){ int n,l; cin>>n>>l; for(int i=1;i<=n;i++){ string s; cin>>s; for(int j=0;j<int(s.size());j++){ bit[i][s[j]-'a'+1]=1; } } long long ans=0; for(int s=1;s<(1<<n);s++){ bitset<30> now((1<<30)-1); for(int j=1;j<=n;j++)if((s>>(j-1))&1)now&=bit[j]; ans+=(count(s)%2?1:-1)*(power(now.count(),l)); ans+=mod; ans%=mod; } cout<<ans; return 0; }
- 1
信息
- ID
- 12445
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 8
- 标签
- 递交数
- 11
- 已通过
- 8
- 上传者