ABC难题

在这里插入图片描述

在这里插入图片描述

题意

给一个序列,给序列里每一种数一个字母"A",“B"或"C”,求有多少种方案使得序列中含有子序列"ABC"。

思路

对于有很多子序列"ABC"的情况,只在第一个"ABC"处统计它。

那么在当前枚举作为"A"的位置前面,不能出现"A"。在当前枚举作为"B"的位置前面,“A"的后面,不能出现"B”。"C"可以不用枚举,只要在"B"之后至少存在一个即可。

记录每种数字是否可以放某种字母,然后就可以方便的统计答案。

至于"C"的问题,可以用所有情况减去一个"C"都没有的情况,就不会T了。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long 
using namespace std;
const ll mod = 1e8+7;
const int N = 3010;
int n, col[N], tr[N], coln;
int ban[N][3], cntc[N];
ll now, eq0, iv[4], ans;

ll Pow(ll x, ll y) // 快速幂用于求逆元和统计答案
{
    ll ret = 1;
    while (y > 0){
        if (y&1) (ret *= x) %= mod;
        (x *= x) %= mod;
        y >>= 1;
    }
    return ret;
}

void IniBan() // 初始化
{
    memset(ban, 0, sizeof(ban));
    for (int i = 1; i <= coln; i++)
        cntc[i] = 3;
    now = Pow(3, coln);
    eq0 = 0;
}

void Ban(int num, int pos) // pos位置的数字不能填num的字符
{
    ban[col[pos]][num]++;
    if (ban[col[pos]][num] == 1){
        (now *= iv[cntc[col[pos]]]) %= mod;
        cntc[col[pos]]--;
        if (cntc[col[pos]] == 0) eq0++;
        else (now *= cntc[col[pos]]) %= mod;
    }
}

void Rlv(int num, int pos) // relieve,与ban相反
{
    ban[col[pos]][num]--;
    if (ban[col[pos]][num] == 0){
        if (cntc[col[pos]] == 0) eq0--;
        else (now *= iv[cntc[col[pos]]]) %= mod;
        cntc[col[pos]]++;
        (now *= cntc[col[pos]]) %= mod;
    }
}

int main()
{
    iv[1] = 1; iv[2] = Pow(2, mod-2); iv[3] = Pow(3, mod-2);
    cin >> n;
    coln = 0;
    memset(tr, 0, sizeof(tr));
    for (int i = 1; i <= n; i++){
        cin >> col[i];
        if (!tr[col[i]]) tr[col[i]] = ++coln;
        col[i] = tr[col[i]];
    }
    ans = 0;
    for (int i = 1; i <= n; i++){
        IniBan();
        for (int j = 1; j < i; j++)
            Ban(0, j);
        Ban(1, i); Ban(2, i);
        for (int j = i+1; j <= n; j++){
            if (j > i+1){
                Rlv(0, j-1);
                Rlv(2, j-1);
                Ban(1, j-1);
            }
            Ban(0, j); Ban(2, j);
            if (eq0 == 0) (ans += now) %= mod;
        }
    }
    for (int i = 1; i <= n; i++){
        IniBan();
        for (int j = 1; j < i; j++)
            Ban(0, j);
        Ban(1, i); Ban(2, i);
        for (int j = i+1; j <= n; j++)
            Ban(2, j);
        for (int j = i+1; j <= n; j++){
            if (j > i+1){
                Rlv(0, j-1);
                Rlv(2, j-1);
                Ban(1, j-1);
            }
            Ban(0, j);
            if (eq0 == 0) (ans -= now-mod) %= mod;
        }
    }
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyyxyyx/article/details/83089288
ABC