Tinkoff Challenge - Final Round (Codeforces Round #414, rated, Div. 1 + Div. 2) 题解

68 篇文章 0 订阅
50 篇文章 0 订阅

A:

有一个强盗企图抢劫一个银行,然后他失败了( wtf...? )
这导致这个银行的顾客 Oleg 想要搞点事情。。?
现在有很多个保险箱排成一列每个保险箱要么是空的要么有一些支票
现在 Oleg 在第 b 个保险箱的位置
同时在位置a,a<b以及位置 c,b<c 各有一名保安
这两个保安很懒。。所以他们并不想动
Oleg 每次可以选择向左或者向右移动一格
请问他最多能拿到多少支票???
1a<b<c109
共有 n1n105 张支票,第 i 张支票位于保险箱xi

Solution A:

显然夹在 a,c 之间的支票都是拿得到的
于是扫描一遍就行了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    int a,b,c,n,x,Ans = 0;
    cin >> a >> b >> c >> n;
    while (n--)
    {
        scanf("%d",&x);
        Ans += (b < x && x < c) ? 1 : 0;
    }
    cout << Ans << endl;
    return 0;
}

B:

Igor 养了 n 只兔子
大家都知道兔子喜欢吃胡萝卜
于是Igor买了个一根胡萝卜
这根胡萝卜可以看做一个底边长为 1 高为h的等腰三角形
Igor 想通过切 n1 刀把这根胡萝卜分成面积相等的 n
并且每刀都平行于底边
请输出每刀离等腰三角形顶部的距离
2n10001h105

Solution B:

将这个等腰三角形的顶点作为原点
中垂线方向作为 x 轴正方向,建立平面直角坐标系
首先每只兔子能分到的胡萝卜面积为定值S=h2n
处于 x 轴正上方的那条直线解析式显然为f(x)=12hx
每只兔子分到的胡萝卜都可以看做一个梯形
设当前这一刀切在距离顶点 a 的地方
设下一刀离当前距离为x
那么就是解方程 [f(a)+f(a+x)]x=S
展开发现是个一元二次方程
用判别式暴力解过去就行了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

typedef double DB;

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    int n,h; cin >> n >> h;
    DB A = 0,tmp = 4.00 * (DB)(h) * (DB)(h) / (DB)(n);
    for (int i = 1; i < n; i++)
    {
        DB x = (-2.00 * A + sqrt(4.00 * A * A + tmp)) / 2.00;
        A += x; printf("%.12lf ",A);
    }
    return 0;
}

C:

Oleg Igor 打算开个公司
首先得给他们的公司取个名字
Oleg Igor 各有一个大小为 n 的可重字符集合
他们打算通过一个游戏确定一个长度为n的公司名称
一开始,公司的名字可以看做一个长度为 n 全是的字符串
每一次, Oleg 或者 Igor 可以选择自己集合的一个字符填充任意一个
游戏从Oleg先手,两人轮流操作
Oleg 的目的是让公司名称的字典序尽可能小
Igor 的目的是让公司名称的字典序尽可能大
请问最后确定的公司名称会是什么?
1n3105

Solution C:

对于 Oleg ,将他的字符集按照升序排好,显然他只会用前 n2
对于 Igor ,将他的字符集按照降序排好,显然他只会用前 n2
有一个直观的贪心
每次轮到 Oleg ,把剩余字符中最小的填在第一位
每次轮到 Igor ,把剩余字符中最大的填在第一位
不过显然有特殊情况
如果 Igor 剩余的所有字符都不大于 Oleg 剩下的
那么 Oleg 拿剩余最小的填在第一位显然不优
那么就拿一个最大的填在最后面,把前面的留给对手
对于 Igor 也是一样的策略
因此加个特判就行了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 3E5 + 3;

int n;
char A[maxn],B[maxn],C[maxn];

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    scanf("%s",A + 1); scanf("%s",B + 1); n = strlen(A + 1);
    sort(A + 1,A + n + 1); sort(B + 1,B + n + 1); reverse(B + 1,B + n + 1);
    int L = 1,R = n,la = 1,ra = n + 1 >> 1,lb = 1,rb = n >> 1;
    for (int i = 1; i <= n; i++)
    {
        if (i & 1)
        {
            if (A[la] >= B[lb]) C[R--] = A[ra--];
            else C[L++] = A[la++];
        }
        else
        {
            if (B[lb] <= A[la]) C[R--] = B[rb--];
            else C[L++] = B[lb++];
        }
    }
    for (int i = 1; i <= n; i++) putchar(C[i]);
    return 0;
}

D:

Oleg 有一张 n 个点m条边的无向连通图
现在他想给图中每个点一个标号
记第 i 个点的标号为xi
对于图中任意一个点对 (u,v)(uv) ,需要满足
(u,v) 有边相连,则 |xuxv|1
否则 |xuxv|>1
Oleg 想知道是否存在一个这样的合法方案
如果有,那么输出任意一种方案
1n,m3105

Solution D:

我们称一个团为极大的,当对于这个团,不能再加入任意一个点
显然,对于图中任意一点,若它被三个或以上的极大团包含,一定无解
证明的话,每个团拿出一个不在其它团出现的点,发现无法标号
这样的点一定存在,否则不满足极大团性质。
那么对于每个点,可以暴力搜索出包含它的每一个极大团
因为每个点最多属于两个不同的极大团,所以如此暴力是 O(n)
假设原图一共有 k 个不同的极大团
对于任意两个极大团,如果它们的交集不为空,
就把交集的点提出来,连边团A,交集,团 B
那么这样就构建出一张新图
显然,新图是一条链,或者链上加一个环
如果链上有环,显然可以发现无法标号的。。
那么有解就是只剩下一条链的情形
从左到右逐个递增地标号,显然就符合题意了
然后。。。此题的数据居然没有卡新图出现环的情形!!!
于是刚才自己把自己的代码给叉掉了。。。。。。。。
wtf.....????

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 3E5 + 3;

int n,m,tp,cnt,tot,vis[maxn],Ans[maxn],du[maxn*2];
bool bo[maxn];

map <pair<int,int>,int> M;
map <pair<int,int>,bool> mp;
vector <int> h[maxn],v[maxn],g[maxn*2],G[maxn];

inline void Dfs(int x)
{
    int Cnt = cnt + 1,nex = 0;
    for (int i = 0; i < v[x].size(); i++)
    {
        int now = v[x][i];
        if (vis[now] != cnt) continue;
        vis[now] = Cnt; if (!nex) nex = now;
    }
    cnt = Cnt; if (!nex) return;
    if (h[nex].size() == 2) {puts("NO"); exit(0);}
    g[tp].push_back(nex); Dfs(nex);
}

inline void Work(int i)
{
    ++cnt;
    for (int j = 0; j < v[i].size(); j++) vis[v[i][j]] = cnt;
    for (int j = 0; j < v[i].size(); j++)
    {
        if (mp[make_pair(i,v[i][j])]) continue;
        if (h[i].size() == 2) {puts("NO"); exit(0);}
        g[++tp].push_back(i); g[tp].push_back(v[i][j]); Dfs(v[i][j]);
        for (int l = 0; l < g[tp].size(); l++) h[g[tp][l]].push_back(tp);
        for (int A = 1; A < g[tp].size(); A++)
            for (int B = 0; B < A; B++)
                mp[make_pair(g[tp][A],g[tp][B])] = mp[make_pair(g[tp][B],g[tp][A])] = 1;
        sort(g[tp].begin(),g[tp].end()); return;
    }
}

inline void Dfs2(int x,int from)
{
    if (bo[x]) M[make_pair(x,0)] = ++tot;
    if (G[x].size() == 1) return;
    int nex = G[x][0] == from ? G[x][1] : G[x][0];
    M[make_pair(x,nex)] = M[make_pair(nex,x)] = ++tot;
    Dfs2(nex,x);
}

inline int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    n = getint(); m = getint();
    while (m--)
    {
        int x = getint(),y = getint();
        v[x].push_back(y); v[y].push_back(x);
    }
    for (int i = 1; i <= n; i++)
    {
        Work(i); Work(i);
        for (int j = 0; j < v[i].size(); j++)
            if (!mp[make_pair(i,v[i][j])]) {puts("NO"); return 0;}
    }
    if (tp == 1)
    {
        puts("YES");
        for (int i = 1; i <= n; i++) printf("%d ",1);
        return 0;
    }
    int S,cnt = 0; mp.clear();
    for (int i = 1; i <= n; i++)
    {
        if (h[i].size() == 1) {bo[h[i][0]] = 1; continue;}
        if (mp[make_pair(h[i][0],h[i][1])]) continue;
        ++du[h[i][0]]; ++du[h[i][1]];
        G[h[i][0]].push_back(h[i][1]); G[h[i][1]].push_back(h[i][0]);
        mp[make_pair(h[i][0],h[i][1])] = mp[make_pair(h[i][1],h[i][0])] = 1;
    }
    for (int i = 1; i <= tp; i++)
        if (du[i] == 1) S = i,++cnt;
    if (cnt < 2) {puts("NO"); return 0;}
    M[make_pair(S,0)] = ++tot;
    M[make_pair(S,G[S][0])] = M[make_pair(G[S][0],S)] = ++tot;
    Dfs2(G[S][0],S); puts("YES");
    for (int i = 1; i <= n; i++)
        printf("%d ",h[i].size() == 1 ? M[make_pair(h[i][0],0)] : M[make_pair(h[i][0],h[i][1])]);
    return 0;
}

这份代码判了新图出现环。。。虽然不影响 AC 。。。???


E:

Oleg Igor 打算赠送给 zscoder 一件礼物
这个礼物可能是一杯胡萝卜汁
两个人一共买了 n 根胡萝卜,把它们排成一行
i根胡萝卜的果汁度为 ai
Oleg 希望礼物的果汁度越大越好
然而 Igor 却和他相反
于是两个人决定玩一个游戏
每一回合,每个人可以选择吃掉队头或者队尾的胡萝卜
当只剩下最后一根胡萝卜的时候,就把它做成胡萝卜汁送给 zscoder
游戏总是由 Oleg 先手
不过 Oleg 打算趁着 Igor 上厕所的机会先偷偷操作 k
每次也是吃掉队头或者队尾的萝卜
对于每个0k<n,输出对于 Oleg ,能获得的最优答案
1n3105,1ai109

Solution E:

先考虑一个简单版的问题,如果只有这 n 根萝卜,最后剩下的会是多少
可以二分一个答案,然后对于所有的萝卜,
如果果汁度小于当前答案,标记为0,否则为1
那么做一个区间dp,如果最后剩下的是1,说明可行
但是这个区间 dp O(n2) 的,肯定有快速判断的办法?
给出一个结论:
n 为奇数,则结果为1,当中间位置和中间位置两边的位置有一个,为1
n为偶数,则结果为1,当中间两个位置都是1
第一种情况,先手先吃掉不为1那一侧的萝卜,
然后每次吃和对手不同侧就行了
第二种情况,先手随意吃一个,后续每次和对手不同侧
那么这样甚至连二分都不用了,只需对于中间位置的果汁度写写特判
那么枚举中间位置,发现这种情况能更新到的 k 的值是连续的一段
于是,只需要写个后缀转移优化就可以解决了

#include<iostream>
#include<cstdio>
#include<cmath>
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
using namespace std;

const int maxn = 3E5 + 3;

int n,A[maxn],A0[maxn],A1[maxn],Ans[maxn];

inline void Modify(int now,int len)
{
    if (len & 1) A1[len + 1 >> 1] = max(A1[len + 1 >> 1],now);
    else A0[(len >> 1) + 1] = max(A0[(len >> 1) + 1],now);
}

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    cin >> n;
    for (int i = 1; i <= n; i++) scanf("%d",&A[i]);
    for (int i = 1; i < n; i++)
    {
        int now = max(A[i],A[i + 1]);
        int Max = min(i - 1,n - i - 1) << 1;
        Modify(now,n - Max - 2);
    }
    for (int i = 2; i < n; i++)
        if (A[i - 1] < A[i] && A[i + 1] < A[i])
        {
            int now = max(A[i - 1],A[i + 1]);
            int Max = min(i - 2,n - i - 1) << 1;
            Modify(now,n - Max - 3);
        }
        else
        {
            int Max = min(i - 2,n - i - 1) << 1;
            Modify(A[i],n - Max - 3);
        }
    for (int i = 1; i <= n; i++) Modify(A[i],n - 1);
    for (int i = 1; (i - 1 << 1) < n; i++)
        A0[i] = max(A0[i],A0[i - 1]),Ans[i - 1 << 1] = A0[i];
    for (int i = 1; (i << 1) - 1 < n; i++)
        A1[i] = max(A1[i],A1[i - 1]),Ans[(i << 1) - 1] = A1[i];
    for (int i = 0; i < n; i++) printf("%d%c",Ans[i],i == n - 1 ? '\n' : ' ');
    return 0;
}

F:

Leha是一个黑客,他想要修改一家银行的数据
具体的,这家银行目前有 n 个客户,第i个客户目前的存款为 ai
可以假设这些数字排成一行
每次, Leha 可以做一下两个操作的其中一种:
1.对于区间 [l,r] ,将每个客户存款中的数字 x 全部变成数字y
即假设 x=2,y=3 ,那么数字 233 将变成 333
2.询问区间 [l,r] 的存款总和
1n,q1051ai109
注意操作1中 x0 恒成立

Solution F:

对于每个区间,定义 sum[o][i] 各个位置数字 i 的贡献和
定义Mark[o][i],修改操作会让所有数字 i 变成Mark[o][i]
那么转移就很简单了,显然可以合并,
那么 Mark[son][i]=Mark[o][Mark[son][i]]
反正剩下的所有问题都交给线段树
复杂度 O(10nlogn)

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;

const int T = 4;
const int N = 10;
const int maxn = 1E5 + 10;
typedef long long LL;

int n,m,a[maxn],Mark[maxn*T][N];
LL mi[N],sum[maxn*T],c[maxn*T][N],dt[N],ML[N],MR[N];

inline void Build(int o,int l,int r)
{
    if (l == r)
    {
        for (int now = 0,x = a[l]; x; x /= 10,now++)
            c[o][x % 10] += mi[now];
        for (int i = 0; i < N; i++) Mark[o][i] = i;
        sum[o] = a[l]; return;
    }
    int mid = l + r >> 1;
    Build(o<<1,l,mid); Build(o<<1|1,mid+1,r);
    sum[o] = sum[o<<1] + sum[o<<1|1];
    for (int i = 0; i < N; i++)
        Mark[o][i] = i,c[o][i] = c[o<<1][i] + c[o<<1|1][i];
}

inline void pushdown(int o,int l,int r)
{
    for (int i = 0; i < N; i++)
    {
        sum[o] += 1LL * (Mark[o][i] - i) * c[o][i];
        dt[i] -= c[o][i]; dt[Mark[o][i]] += c[o][i];
    }
    if (l == r)
    {
        for (int i = 0; i < N; i++)
            c[o][i] += dt[i],Mark[o][i] = i;
        memset(dt,0,sizeof(dt)); return;
    }
    int lc = o << 1,rc = o << 1 | 1;
    for (int i = 0; i < N; i++)
    {
        c[o][i] += dt[i];
        ML[i] = Mark[o][Mark[lc][i]];
        MR[i] = Mark[o][Mark[rc][i]];
    }
    memset(dt,0,sizeof(dt));
    for (int i = 0; i < N; i++)
        Mark[lc][i] = ML[i],Mark[rc][i] = MR[i],Mark[o][i] = i;
}

inline void Modify(int o,int l,int r,int ql,int qr,int x,int y)
{
    pushdown(o,l,r);
    if (ql <= l && r <= qr)
    {
        Mark[o][x] = y; pushdown(o,l,r); return;
    }
    int mid = l + r >> 1;
    if (ql <= mid) Modify(o<<1,l,mid,ql,qr,x,y); else pushdown(o<<1,l,mid);
    if (qr > mid) Modify(o<<1|1,mid+1,r,ql,qr,x,y); else pushdown(o<<1|1,mid+1,r);
    sum[o] = sum[o<<1] + sum[o<<1|1];
    for (int i = 0; i < N; i++) c[o][i] = c[o<<1][i] + c[o<<1|1][i];
}

inline LL Query(int o,int l,int r,int ql,int qr)
{
    pushdown(o,l,r);
    if (ql <= l && r <= qr) return sum[o];
    LL ret = 0; int mid = l + r >> 1;
    if (ql <= mid) ret = Query(o<<1,l,mid,ql,qr);
    if (qr > mid) ret += Query(o<<1|1,mid+1,r,ql,qr);
    return ret;
}

inline int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    n = getint(); m = getint(); mi[0] = 1;
    for (int i = 1; i < N; i++) mi[i] = mi[i - 1] * 10LL;
    for (int i = 1; i <= n; i++) a[i] = getint(); Build(1,1,n);

    while (m--)
    {
        int typ = getint(),l,r;
        l = getint(); r = getint();
        if (typ == 1)
        {
            int x = getint(),y = getint();
            Modify(1,1,n,l,r,x,y);
        }
        else printf("%I64d\n",Query(1,1,n,l,r));
    }
    return 0;
}

G:

给出两个仅由 A,B 组成的字符串 x,y
定义一对二进制数码 (s,t) 是好的,当满足以下条件:
1. s,t 仅由 0,1 组成
2. 1|s|,|t|n
3. 当把 x,y 中的每个 A 替换成s,每个 B 替换成t,有 x=y
现在 Igor 有两个字符串 (c,d)
每个字符串由 A,B,? 三种字符组成
首先, Igor 需要把所有的 ? 替换成A,B中的一种
然后,对于每种方案,定义它的灵活度,为好的 (s,t) 的数量
现在 Igor 想知道,对于每种可行的方案,它们的灵活度的和是多少

Solution G:

定义二进制数码对 (s,t) 是互质的,当:
1. s=t
2. 若 |s|<|t| ,则 t=s+x (s,x) 互质
3. 若 |s|>|t| ,则 (t,s) 是互质的
对于字符串对 (c,d)
若存在一种方案,使得最终 c=d
那么显然任意的 (s,t) 都符合条件,先放在一边
那么需要考虑的只有 cd 的情形
每一次,将 (c,d) 的最长公共前缀删除
因为 cd ,因此对于剩下的串,有 c[0]d[0]
那么肯定是一个 s 另一个t
不妨假设 t=s+x ,于是就变成了 (s,x) 的子问题
那么当 cd ,显然所有满足的方案有 (s,t) 互质
对于互质数码对 (s,t) ,有个性质, s+t=t+s
也是类似地无限递归来证明
于是对于 (c,d) ,都可以写成 sssttt 的形式
对于 c 假设它有x1 s y1 t ,类似地假设x2,y2
x1>x2 ,显然要有 y1<y2
对于 x1=x2 并且 y1=y2 的情形稍后讨论
x1<x2 并且 y1>y2 的情形显然类似
那么接下来讨论 x1>x2 并且 y1<y2 时的解法
x=x1x2y=y2y1
那么合法的 (s,t) 等价于需要满足 xs=yt
一样的,不妨假设 |s|<|t|t=s+x
显然需要 x>y ,那么就有 (xy)s=yx
于是对 (x,y) 的求解变成了对 (xy,y) 的求解
(x,y) 不互质,显然我们将它们消到互质也是不影响答案的
因此假设 (x,y) 互质,那么最终就会变成对 (1,1) 的求解
这时需要两边的二进制数码一样,设为 K
把这个K倒带回去,原方程显然就变成了 x(yK)=y(xK)
就是左边为 K 重复y次而右边重复 x
那么合法的K就是重复了 max(x,y) 次后
长度仍然不超过 n 的所有二进制数码
这样的二进制数码显然有2nmax(x,y)+12
那么还需要解决的,就是 x1=x2 并且 y1=y2 的情形
这时,任意的满足互质性质的 (s,t) 都符合题意
|s|=p|t|=q ,则满足条件的数码对共有 2gcd(p,q)
证明的话,不妨假设 p<q ,那么 t=s+x
就变成证明 2gcd(p,q)=2gcd(qp,p) 递归下去就好了
因此这种情况就变成求 ni=1nj=12gcd(i,j)
关于这个式子。。暴力上莫比乌斯反演然后求和 O(n34) 就行了
不过这个太麻烦了。。。也太经典,就不说了
题解有给出一个 O(nlogn) 的算法
不过这个就更简单了。。。反正。。随便就写出来了
最后,设 c 中有a ? d中有 b
p=x1x2q=y1y2
c 中有x ? 填了A d 中有y
那么就需要解决 (p+xy,q+(ab)(xy))
真正会改变的就只有 d=xy ,显然 d[b,a]
那么 d 就可以枚举了
对于不同的d,解法就是按上面两种情况分类讨论
而对于分配方法,显然是 CxaCxdb=CxaCb+dxb
众所周知, ax=0CxaCb+dxb=Cb+da+b
至此,这个问题就解决了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

typedef long long LL;
const LL mo = 1000000007;
const int maxn = 3E5 + 3;
const int maxm = maxn * 2;

int S,T,sa,sb,sc,ta,tb,tc,n,tot,sm,sum,Ans
    ,mu[maxn],pri[maxn],Fac[maxm],Inv[maxm],cnt[maxn],mi[maxn];
bool not_pri[maxn]; char s[maxn],t[maxn];

#define Mul(x,y) (1LL * (x) * (y) % mo)
#define max(a,b) ((a) > (b) ? (a) : (b))
#define Add(x,y) ((x) + (y) < mo ? (x) + (y) : (x) + (y) - mo)
#define Dec(x,y) ((x) - (y) >= 0 ? (x) - (y) : (x) - (y) + mo)

inline int gcd(int x,int y) {return y ? gcd(y,x % y) : x;}

inline int C(int N,int M)
{
    return Mul(Fac[N],Mul(Inv[M],Inv[N - M]));
}

int ksm(int x,int y)
{
    int ret = 1;
    for (; y; y >>= 1)
    {
        if (y & 1) ret = Mul(ret,x);
        x = Mul(x,x);
    }
    return ret;
}

void Pre_Work()
{
    scanf("%s",s + 1); S = strlen(s + 1);
    scanf("%s",t + 1); T = strlen(t + 1); cin >> n;
    for (int i = 1; i <= S; i++)
        if (s[i] == 'A') ++sa;
        else if (s[i] == 'B') ++sb;
        else ++sc;
    for (int i = 1; i <= T; i++)
        if (t[i] == 'A') ++ta;
        else if (t[i] == 'B') ++tb;
        else ++tc;
    if (S == T)
    {
        sm = 1;
        for (int i = 1; i <= S; i++)
            if (s[i] == '?' && t[i] == '?') sm = Mul(sm,2);
            else if (s[i] == 'A' && t[i] == 'B') sm = 0;
            else if (s[i] == 'B' && t[i] == 'A') sm = 0;
    }
    Fac[0] = 1; for (int i = 1; i <= sc + tc; i++) Fac[i] = Mul(Fac[i - 1],i);
    Inv[sc + tc] = ksm(Fac[sc + tc],mo - 2);
    for (int i = sc + tc - 1; i >= 0; i--) Inv[i] = Mul(Inv[i + 1],i + 1);
    mu[1] = 1; for (int i = 1; i <= n; i++) cnt[i] = Mul(n / i,n / i);
    for (int i = 2; i <= n; i++)
    {
        if (!not_pri[i])
            pri[++tot] = i,mu[i] = -1;
        for (int j = 1; j <= tot; j++)
        {
            int Nex = pri[j] * i;
            if (Nex > n) break; not_pri[Nex] = 1;
            if (i % pri[j] == 0)
            {
                mu[Nex] = 0; break;
            }
            mu[Nex] = -mu[i];
        }
    }
    mi[0] = 1;
    for (int i = 1; i <= n; i++)
    {
        int now = 0; mi[i] = Mul(mi[i - 1],2);
        for (int j = 1,last = n / i; j <= last; j++)
            if (mu[j] == 1) now = Add(now,cnt[i * j]);
            else if (mu[j] == -1) now = Dec(now,cnt[i * j]);
        sum = Add(sum,Mul(now,mi[i]));
    }
    mi[n + 1] = Mul(mi[n],2);
}

inline int Solve(int p,int q,int d)
{
    if (!p && !q)
    {
        int A = Dec(C(sc + tc,tc + d),sm);
        int B = Mul(Dec(mi[n + 1],2),Dec(mi[n + 1],2));
        return Add(Mul(A,sum),Mul(sm,B));
    }
    else if (p <= 0 && q <= 0) return 0;
    else if (p >= 0 && q >= 0) return 0;
    else
    {
        p = abs(p); q = abs(q);
        int g = gcd(p,q),A = C(sc + tc,tc + d);
        p /= g; q /= g; int B = Dec(mi[n / max(p,q) + 1],2);
        return Mul(A,B);
    }
}

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    Pre_Work(); int p = sa - ta,q = sb - tb + sc - tc;
    for (int d = -tc; d <= sc; d++) Ans = Add(Ans,Solve(p + d,q - d,d));
    cout << Ans << endl;
    return 0;
}

关于使用莫比乌斯反演 O(n34) 求和的方式可以看这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值