CF383E 状压dp(SOSdp)

本文介绍了如何利用状态压缩和SOS动态规划解决一个关于n个长度为3的字符串问题,通过将字符串转化为24位二进制表示,计算每个关键字符方案的正确字符串个数的平方异或和。解题步骤包括压缩、状态转移和最终答案计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

n(≤1e4)n(\le1e4)n(1e4)个长度为333的字符串(字符为:′a′−′x′'a'-'x'ax(24个字符)),当一个字符串包含至少一个关键字符,则这个字符串是正确的,而关键字符的方案数为2242^{24}224,对每个方案的正确字符串个数平方求异或和

解题思路:

  • 首先非常显而易见的是,可以将字符串先压缩成长度为24的2进制串,而对于每个方案也可以这样表示
  • 对于每个方案表示的状态ststst,实际上是求∑st&i!=0ai\sum_{st \& i != 0}a_ist&i!=0aiaia_iai为状态为iii的字符串有多少个,即与ststst有交集的字符串个数多少
  • 通过状压dpdpdp(这道题里称作SOSdpSOSdpSOSdp),可以求出dp[i]=∑st&i=iaidp[i]=\sum_{st \& i = i}a_idp[i]=st&i=iai,即状态iii子集的贡献个数
  • 知道dp[i]dp[i]dp[i],那么dp[∼i]dp[\sim i]dp[i]就为不与i有交集的字符串个数,所以n−dp[∼i]n-dp[\sim i]ndp[i]就为与iii有交集的字符串个数
  • 最后统计答案就好
  • 注意dpdpdp顺序,正序会统计重复,逆序就不会

AC代码:

#include <bits/stdc++.h>
#define ft first
#define sd second
#define pb push_back
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) //不能跟puts混用
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 1e5 + 10;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
int n, dp[(1 << 24) + 100];
string s;
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> s;
        int res = 0;
        for (int j = 0; j < 3; j++) res |= (1 << (s[j] - 'a'));
        dp[res]++;
        cout << res << endl;
    }
    for (int j = 0; j < 24; j++)
        for (int i = (1 << 24); i > 0; i--) { //倒着来就不会有重复
            if ((i >> j) & 1) 
                dp[i] += dp[i ^ (1 << j)];
    }
    int ans = 0;
    for (int i = 0; i <= 56; i++) cout << dp[i] << endl;
    for (int i = 0; i < (1 << 24); i++) ans ^= (n - dp[i]) * (n - dp[i]);
    cout << ans << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值