zoukankan      html  css  js  c++  java
  • AtCoder AGC020 E-Encoding Subsets

    题目链接

    AGC020 E-Encoding Subsets

    题目大意

    给出一个 (01)​​​​​ 串 (S)​​​​​,对于串中连续出现 (Kgeq 2)​​​​​ 次的子串 (P)​​​​​,可以将 (PP...P)​​​​​ 改写成 ((P imes K))​​​​​ ,如 001001001 可被改写成 00(1(0x2)x2)1(001x3) 。现在对于所有和 (S)​​​​​ 长度相同且 (Swedge T=T)​​​​​ 的串 (T)​​​​​​​,求出改写方案数的总和,答案对 (10^9+7)​​​​​ 取模。

    (1leq |S|leq 100)

    思路

    先考虑对于一个串 (S) 如何单独计算答案,这个不难,容易想到用区间 (DP) 做。设 (dp_{i,j})([i,j])​ 内的串的改写方案数,转移时强制区间开头连续的一段是要被改写的即可,通过合适的预处理可以做到 (O(n^3log n))

    当你尝试分析如何快速地把答案加起来的时候,会发现当 (S) 有一位发生翻转后,答案会截然不同,两者是没有什么继承关系的,所以分开计算答案没有前途!既然分开来不大能做,那就尝试合起来一起计算答案。

    (f(S))​​​​​​​​​​​ 为当串为 (S)​​​​​​​​​​​ 时题目对应的答案,转移还是强制 (S)​​​​​​​​​​​​ 开头被改写,注意到现在 (f(S))​​​​​​​​​​​ 代表了 (S)​​​​​​​​​​​​ 所有子集的答案,所以在折叠的时候,需要满足的条件是 (PP...P)​​​​​​​​​​ 为 (S)​​​​​​​​​​ 对应前缀的子集,这等价于 (P)​​​​​​​​​​ 是 (S_{1...|P|}wedge S_{|P|+1...2|P|}wedge...wedge S_{(K-1)|P|+1,K|P|})​​​​​​​​​ 的子集​,而这个刚好存在此与和的 (f)​​​​​ 里面,所以直接用 (f)​​​​​​​ 值转移即可。实际转移时还需考虑开头不折叠的情况,设 (w(S,K,|P|)) 表示前面的那个与和,则有:

    [f(S)=(1+S_1)f(S_{2...|S|})+sum_{|P|=1}^{|S|}sum_{K=2}^{lfloorfrac{|S|}{|P|} floor}f(w(S,K,|P|))cdot f(S_{K|P|+1...|S|}) ]

    记号和官方题解做到了高度统一(不过他把 (K=2) 写成了 (K=1)

    这里时间复杂度看起来上限是 (O(2^{|S|+1})) 的,感觉这个题最重要的地方就是,你要看出来这个做法其实是 (O()能过())​ 的,进而分析出其真正的复杂度,而不是被假上限给吓跑了。

    注意到所有会被用到的 (T) 都是由原串 (S) 导出来的,可以很直观地感受到长度比较长的串其实非常少,比如长度 (>frac{|S|}{2}) 的串只有 (O(n)) 个,如果允许折叠一下,那么长度 (>frac{n}{4}) 的串有 (O(n^2)) 个,这样平衡一下,可以发现折两次的时候时间复杂度上限降到最低,(3) 种折法可以视为常数,则时复为 (O(2^{frac{n}{8}}+n^3)),运算量仅有 (7e5),可以轻松通过此题。

    Code

    实现的时候用了哈希表配记忆化搜索,自定义哈希的内容来自 (color{black}{n}color{red}{eal}),然而并没有比 (map) 快多少诶。

    #include<iostream>
    #include<chrono>
    #include<unordered_map>
    #include<vector>
    #define rep(i,a,b) for(int i = (a); i <= (b); i++)
    #define per(i,b,a) for(int i = (b); i >= (a); i--)
    #define N 110
    #define ll long long
    #define mod 998244353
    #define modd 1000000007
    using namespace std;
    
    struct custom_hash{
        static uint64_t splitmix64(uint64_t x){
            x += 0x9e3779b97f4a7c15;
            x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
            x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
            return x ^ (x >> 31);
        }
        size_t operator () (uint64_t x) const{
            static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
            return splitmix64(x + FIXED_RANDOM);
        }
        size_t operator () (pair<uint64_t, uint64_t> x) const{
            static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
            return splitmix64(splitmix64(x.first + FIXED_RANDOM) ^ (x.second + FIXED_RANDOM));
        }
    };
    unordered_map<pair<ll, ll>, int, custom_hash> dp;
    
    pair<ll, ll> val(vector<int> p){
        ll ret1 = 1, ret2 = 1;
        for(int x : p) ret1 = (ret1*3 + x)%mod, ret2 = (ret2*3 + x)%modd;
        return make_pair(ret1, ret2);
    }
    
    vector<int> sub(vector<int> p, int l, int r){
        return {p.begin()+l, p.begin()+r};
    }
    
    int dfs(vector<int> p){
        pair<ll, ll> id = val(p); int n = p.size();
        if(dp[id]) return dp[id];
        dp[id] = (1+p.front()) * dfs(sub(p, 1, n)) % mod;
        rep(len,1,n) rep(k,2,n/len){
            vector<int> q(len, 1);
            rep(i,0,len-1) rep(j,0,k-1) q[i] &= p[j*len+i];
            (dp[id] += (ll) dfs(q) * dfs(sub(p, len*k, n)) % mod) %= mod;
        }
        return dp[id];
    }
    
    int main(){
        string s; cin>>s;
        vector<int> p;
        rep(i,0,(int)s.size()-1) p.push_back(s[i]-'0');
        dp[make_pair(1, 1)] = 1;
        cout<< dfs(p) <<endl;
        return 0;
    }
    
  • 相关阅读:
    哲学的初步认识7
    随机法解决TSP问题
    哲学的初步认识6
    dfs+dp思想的结合------hdu1078
    动态规划3-------poj1050
    动态规划2-----hdu1069
    动态规划1-----------poj1080
    js中Math.round、parseInt、Math.floor和Math.ceil小数取整小结【转】
    美术馆
    无刷新评论
  • 原文地址:https://www.cnblogs.com/Neal-lee/p/15147472.html
Copyright © 2011-2022 走看看