首页 > 其他分享 >洛谷P4199 万径人踪灭

洛谷P4199 万径人踪灭

时间:2023-12-09 19:55:05浏览次数:35  
标签:std 满足条件 洛谷 人踪 int FFT constexpr 万径 return

题目链接

考虑容斥:拿满足条件 \(1\) 的方案数减去满足条件 \(1\) 但不满足条件 \(2\) 的方案数就是答案。

满足条件 \(1\) 但不满足条件 \(2\) 的方案可以用 \(\text{Manacher}\) 算法 \(O(n)\) 计算。

对于满足条件 \(1\) 的总方案数,我们记 \(c_i\) 表示以 \(i\) 位置为对称轴时,满足条件的位置对的数量(即两个位置上的字符相同,且 \(i\) 位置是它们的对称轴),那么显而易见,答案就是 \(\sum (2^{f_i}-1)\)。

所以现在的问题就是 \(c_i\) 怎么求。我们枚举每一种字符并设其为 \(ch\),之后将序列中对应位置字符为 \(ch\) 的位置标为 \(1\),将新数列与其翻转后的数列加法卷积一下就可以得到只考虑某种字符的 \(c'\) 数组。而最终的 \(c\) 数组也就是各个 \(c'\) 数组的加和。

点击查看代码
#include <bits/stdc++.h>
namespace Poly{
    constexpr int N = 2.7e5 + 10;
    constexpr double Pi = acos(-1);
    int n, m, p[N];
    std::complex<double> f[N], g[N];
    void FFT(int n, std::complex<double> *c, int x = 1){
        for(int i = 0; i < n; ++i) if(i < p[i]) std::swap(c[i], c[p[i]]);
        for(int b = 2, k = 1; b <= n; b <<= 1, k <<= 1){
            std::complex<double> t(cos(2 * Pi / b), sin(2 * Pi / b) * x), w(1, 0);
            for(int i = 0; i < n; i += b, w = 1){
                for(int j = 0; j < k; ++j, w *= t){
                    c[i + j] += c[i + j + k] * w;
                    c[i + j + k] = c[i + j] - c[i + j + k] * w - c[i + j + k] * w;
                }
            }
        }
    }
    std::vector<double> Convolution(const auto &a, const auto &b){
        if(!a.size() || !b.size()) return std::vector<double>();
        n = a.size(), m = b.size();
        int l = 1 << (int)ceil(log2(n + m - 1));
        for(int i = 0; i < l; ++i){
            f[i] = i < n? a[i] : 0, g[i] = i < m? b[i] : 0;
            p[i] = (p[i >> 1] >> 1) | (i & 1? l >> 1 : 0);
        }
        n += m - 1, FFT(l, f), FFT(l, g);
        for(int i = 0; i < l; ++i) f[i] *= g[i];
        FFT(l, f, -1); std::vector<double> ret;
        for(int i = 0; i < n; ++i)
            ret.emplace_back(f[i].real() / l);
        return ret;
    }
}
namespace String{
    constexpr int N = 1e5 + 10;
    char s[N << 1]; int n, p[N << 1];
    long long Manacher(char a[]){
        int la = strlen(a);
        s[n = 0] = '$', s[++n] = '#';
        for(int i = 0; i < la; ++i) s[++n] = a[i], s[++n] = '#';
        s[n + 1] = '%';
        int m = 0, r = 0; long long ans = 0;
        for(int i = 1; i <= n; ++i){
            p[i] = (i <= r? std::min(p[m * 2 - i], r - i + 1) : 1);
            while(s[i - p[i]] == s[i + p[i]]) ++p[i];
            if(i + p[i] - 1 > r) m = i, r = i + p[i] - 1;
            ans += (p[i] >> 1);
        }
        return ans;
    }
}
#define FL(i, a, b) for(int i = (a); i <= (b); ++i)
#define FR(i, a, b) for(int i = (a); i >= (b); --i)
using Poly::Convolution;
using String::Manacher;
constexpr int N = 1e5 + 10, p = 1e9 + 7;
int n, m, pw[N], a[N << 1], ans;
char str[N]; std::vector<int> v;
int main(){
    scanf("%s", str);
    n = strlen(str), pw[0] = 1;
    FL(i, 1, n) pw[i] = (pw[i - 1] << 1) % p;
    auto C = [](int x){
        v.resize(n);
        FL(i, 0, n - 1) v[i] = (str[i] - 'a') ^ x;
        auto t = Convolution(v, v);
        FL(i, 0, (n - 1) * 2) a[i] += (int)round(t[i]);
    };
    C(0), C(1);
    FL(i, 0, (n - 1) * 2){
        ans = (ans + pw[(a[i] + 1) >> 1] - 1) % p;
    }
    printf("%lld\n", ((ans - Manacher(str)) % p + p) % p);
    return 0;
}

标签:std,满足条件,洛谷,人踪,int,FFT,constexpr,万径,return
From: https://www.cnblogs.com/zac2010/p/17891389.html

相关文章

  • 「杂题乱刷」洛谷P1216
    题目链接一道dp的入门题。\(O(2^n)\):考虑直接爆搜,可以考虑到所有情况。\(O(n^2)\):考虑\(dp\),设\(dp_{i,j}\)代表到达第\(i\)层第\(j\)个数所能达到的最大值。状态转移方程为\(dp_{i,j}=a_{i,j}+\max(dp_{i-1,j-1},dp_{i-1,j})\)。最终答案就是\(\max(dp_{n,1},d......
  • 【洛谷 P2670】[NOIP2015 普及组] 扫雷游戏 题解(模拟)
    [NOIP2015普及组]扫雷游戏题目背景NOIP2015普及组T2题目描述扫雷游戏是一款十分经典的单机小游戏。在行列的雷区中有一些格子含有地雷(称之为地雷格),其他格子不含地雷(称之为非地雷格)。玩家翻开一个非地雷格时,该格将会出现一个数字——提示周围格子中有多少个是地雷格。游戏的......
  • 【LGR-168-Div.4】洛谷入门赛 #18
    打表过样例题目描述很不幸,你遇到了不负责任的出题人。在某道试题里,共有\(N\)个测试点,组成了\(k\)个Subtask,第\(i\)个Subtask包含\(p_i\)个测试点,第\(j\)个测试点的编号为\(w_{i,j}\)。请注意,一个测试点可能属于多个Subtask。Subtask每个Subtask包含多个测......
  • 「杂题乱刷」洛谷P1544
    题目链接数字三角形的变形。直接在原来的基础上加个判断\(3\)倍的就行了。参考代码:点击查看代码#include<bits/stdc++.h>usingnamespacestd;longlongn,m,ans=-1e18,a[110][110],dp[110][110][5010];#definelc(x)x<<1#definerc(x)x<<1|1#definelowbit(x)x&-......
  • 「杂题乱刷」洛谷P2285
    题目传送门一道小清新动态规划题,直接设\(dp[i]\)表示前\(i\)个鼹鼠最多能打到几个,然后状态转移方程也很好想了。参考代码:点击查看代码#include<bits/stdc++.h>usingnamespacestd;longlongn,m,ans,dp[10010],x[10010],y[10010],times[10010];#definelowbit(x)x&-......
  • 「杂题乱刷」洛谷P1064
    题目传送门一道算是dp的板子题了。题意大概就是01背包+捆绑。首先回顾一下01背包,一个比较基础的dp题,状态转移方程也很好想,是\(dp[i][j]=\max(dp[i][j],dp[i-1][j-w[i]]+v[i])\)。代码实现如下:点击查看代码#include<bits/stdc++.h>usingnamespacestd;longlo......
  • 洛谷 P1044 [NOIP2003 普及组] 栈 题解
    洛谷P1044[NOIP2003普及组]栈题解Sol本题通过分析可得:假设现在进行\(12\)次操作,我们把push认为是在地图上向右走,pop向上走,那么其中一个合法的步骤可以是(\(p1\)代表push,\(p2\)代表pop):\(p1,p1,p2,p1,p2,p2,p1,p1,p2,p2,p1,p2\)。而且我们发现,他最终会......
  • 洛谷P1443 马的遍历(BFS广度优先搜索)
    这道题只要求输入最小步数即可,而且数据的个数较大,所以优先采用BFS(广度优先搜索):广度优先搜索,即以数据搜索的广度优先,换句话说就是每一次操作都将所有可能的结果存储下来,随后对数据进行下一步处理,注意是对每组数据都只进行一次处理,如果是一条路走到头,这就变成了深度优先搜索(DFS)。而基......
  • [洛谷P5966] [BZOJ4344] [POI2016] Hydrorozgrywka
    题解建出原图的圆方树。由于原图无重边,不妨把桥看作二元环建树,这样圆点只与方点直接相连。圆方树定某一圆点为根后,若点\(u\)是圆点,定义点\(u\)的子仙人掌为点\(u\)子树中的圆点在原图的导出子图,定义该子仙人掌的根为点\(u\);若点\(u\)是方点,定义点\(u\)的子仙人掌为点......
  • P1090 [NOI洛谷P2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
    可以用堆来实现,或者更简单一点的优先队列优先队列:#include<queue>priority_queue<int,vector<int>,greater<int>>q;因为每次合并后,最小的两个值都会更新,所以可以很好的利用优先队列内元素有序的特点,减少因反复排序带来的时间复杂度;一开始我用STL自带的排序sort,报了5个TLE,后来意......