ABC425 题解
Solution
E
模数不是质数。EXCRT?
考虑排好了前 \(i-1\) 个颜色,插入第 \(i\) 个颜色的方案数。定义 \(sum=\sum_{k=1}^{i-1}{C_k}\),由插板法得答案为 \(\binom{sum}{C_i}\)。把每种颜色的答案相乘即可。
F
状压 DP,定义 \(f_{S}\) 为 \(T\) 中元素被选集合是 \(S\) 的方案数。枚举上一个填什么元素即可转移。显然由于部分状态本质是相同的,会有重复计数。
弄个 hash 记录状态对应的子序列,如果是本质相同的状态就不转移。复杂度 \(O(n2^n)\),常数巨大。
我们发现转移时出现了本质相同的状态等价于从序列中删去一个元素,又插入一个元素,且该序列不变。
那么删去和插入属于一个同色连续段,只对每个同色连续段中第一个字符进行转移即可。复杂度 \(O(n2^n)\),300ms 轻松跑过。
G
赛时把 min 看成 max 浪费 30min,糖丸了。
建出 01-Trie,在 01-Trie 上递归求解答案。类似最大异或和的思路,考虑当前需要求答案的区间为 \([0,R)\),字典树上节点为 \(u\),值域为 \([0,2^{dep_u+1})\),则有 \(0 \le R < 2^{dep_u+1}\)。
对叶子节点特判后,分情况讨论:
- \(R \le 2^{dep_u}\)。此时考虑当前节点是否有 \(0\) 子节点,如果有就直接往 \(0\) 子节点走;否则只能往 \(1\) 子节点走,答案要加上 \(R \times 2^{dep_u}\)。
- \(R > 2^{dep_u}\)。此时区间会分裂成 \([0,2^{dep_u}),[2^{dep_u},R)\) 两个小区间。可以再细分成 \(3\) 种情况:
- 当前节点同时有 \(0\) 与 \(1\) 子节点。直接把两个区间往各自的子节点下推。注意 \([2^{dep_u},R)\) 此时就变成了 \([0,R-2^{dep_u})\)。
- 当前节点只有 \(0\) 子节点。这时两个区间都只能往 \(0\) 子节点上推,答案要加上 \(2^{dep_u} \times (R-2^{dep_u})\)。
- 当前节点只有 \(1\) 子节点。同上可得,答案此时需加上 \(4^{dep_u}\)。
但这是 \(O(m)\) 的,会 T。
发现第二种情况有个相同点:答案都有一部分是某个节点 \(x\) 上求和区间为 \([0,2^{dep_x+1})\) 的数答案。预处理出来,就可以 \(O(n \log n + \log m)\) 做了。
Code
E
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5010;
int mod,C[N][N];
void solve()
{
int n,ans=1,sum=0;
cin>>n;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
ans=ans*C[sum+x][sum]%mod;
sum+=x;
}
cout<<ans<<"\n";
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin>>T>>mod;
C[0][0]=1;
for(int i=1;i<=5000;i++)C[i][0]=1;
for(int i=1;i<=5000;i++)
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
while(T--)solve();
return 0;
}
F
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=23,mod=998244353;
int n,f[1<<22];
string s;
int vis[26];
signed main()
{
cin>>n>>s;
f[0]=1;
for(int i=1;i<(1<<n);i++)
{
for(int j=0;j<26;j++)vis[j]=0;
int lst=-1;
for(int j=0;j<n;j++)
if((i>>j)&1)
{
if(vis[s[j]-'a']==0)f[i]=(f[i]+f[i^(1<<j)])%mod;
if(lst!=-1)vis[s[lst]-'a']=0;
vis[s[j]-'a']=1;
lst=j;
}
}
cout<<f[(1<<n)-1]<<"\n";
return 0;
}
G
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,m,tot=1;
int cnt[N<<5],son[N<<5][2];
int val[N<<5];
void insert(int x)
{
int u=1;
for(int i=30;i>=0;i--)
{
int &v=son[u][(x>>i)&1];
if(!v)v=++tot;
cnt[v]++;
u=v;
}
}
void build(int u,int dep)
{
if(dep==-1)
{
val[u]=0;
return;
}
if(cnt[son[u][0]])build(son[u][0],dep-1);
if(cnt[son[u][1]])build(son[u][1],dep-1);
if(cnt[son[u][0]]&&cnt[son[u][1]])val[u]=val[son[u][0]]+val[son[u][1]];
else if(cnt[son[u][0]])val[u]=val[son[u][0]]*2+(1ll<<(2*dep));
else if(cnt[son[u][1]])val[u]=val[son[u][1]]*2+(1ll<<(2*dep));
}
int solve(int u,int dep,int now)
{
if(dep==-1)return 0;
/*
if(now<=(1ll<<dep))
{
if(cnt[son[u][0]])return solve(son[u][0],dep-1,now);
else return solve(son[u][1],dep-1,now)+now*(1ll<<dep);
}
else
{
if(cnt[son[u][0]]&&cnt[son[u][1]])return solve(son[u][0],dep-1,(1ll<<dep))+solve(son[u][1],dep-1,now-(1ll<<dep));
else if(cnt[son[u][0]])return solve(son[u][0],dep-1,(1ll<<dep))+solve(son[u][0],dep-1,now-(1ll<<dep))+(1ll<<dep)*(now-(1ll<<dep));
else if(cnt[son[u][1]])return solve(son[u][1],dep-1,(1ll<<dep))+solve(son[u][1],dep-1,now-(1ll<<dep))+(1ll<<dep)*((1ll<<dep));
}
*/
if(now<=(1ll<<dep))
{
if(cnt[son[u][0]])return solve(son[u][0],dep-1,now);
else return solve(son[u][1],dep-1,now)+now*(1ll<<dep);
}
else
{
if(cnt[son[u][0]]&&cnt[son[u][1]])return val[son[u][0]]+solve(son[u][1],dep-1,now-(1ll<<dep));
else if(cnt[son[u][0]])return val[son[u][0]]+solve(son[u][0],dep-1,now-(1ll<<dep))+(1ll<<dep)*(now-(1ll<<dep));
else if(cnt[son[u][1]])return val[son[u][1]]+solve(son[u][1],dep-1,now-(1ll<<dep))+(1ll<<dep)*((1ll<<dep));
}
return 0;
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
insert(x);
}
build(1,30);
cout<<solve(1,30,m)<<"\n";
return 0;
}

浙公网安备 33010602011771号