2019.10.17模拟赛

T1 论逼格

最近,王第一决定想办法提升自己的逼格。经过数日的研究,他发现:回文数字是非常有逼格的。为了让自己成为一个更有逼格的人,他想要知道:\[S_n = \sum\limits_{i = 1}^{n}i \cdot s_i \cdot [i \% 2]\]
其中,\(s_i\)是长度为\(i\)的回文数个数(不含前导\(0\)),最后面的括号是布尔表达式。
答案对\(233333\)取模
\(T\)组询问,\(T \leq 10^5 ,n \leq 10 ^ 9\)


推式子就好了,最后是个等差乘等比。考场降智最为致命……
考虑长为\(i\)\(s_i\)

$ s_i = 9 * 10 ^ {len -1},len = \lfloor (i + 1) \div 2 \rfloor $
\(S_n = \sum\limits_{i=1}^{n}i \cdot s_i \cdot [i \% 2] = \sum\limits_{i = 1}^{len} (2 \cdot i - 1) \cdot s_i\)

剩下的就是正常等差乘等比的求和公式推导:错位相减

\(\begin{aligned} S_n &= 9 * \sum(2 * i - 1) * 10^{i-1} \\ &=9 * ( 1 + 3 * 10 + 5 * 10 ^ 2 + \cdots + (2n - 1) * 10 ^ {(n - 1)}) \\ 10S_n &= 9 * \sum(2 * i - 1) * 10^{i} \\ &=9 * ( 1 * 10 + 3 * 10 ^ 2 + 5 * 10 ^ 3 + \cdots + (2n - 1) * 10 ^ n)\\ 9S_n &= 9 * (-1 - 2 * 10 - 2 * 10 ^ 2 - 2 * 10 ^ 3 - \cdots + (2n-1)* 1o^n )\\ S_n&=(2n-1)*10^n-2 * \frac{10 * (1 - 10 ^ {n-1})}{1 - 10} -1 \end{aligned}\)

推导完了,这样就可以\(log\)计算了。
另外,\(233333\)不是质数,无法用费马小定理求逆元。

inline int sov(int n)
{
    register int len = qpow(10, n - 1);
    register int len1 = (long long)len * 10 % MOD;
    register int res1 = (long long)(2 * n - 1) * len1 % MOD;
    register int res2 = 20ll * (1 - len) % MOD * RMOD % MOD;
    return (res1 + res2 - 1 + MOD) % MOD;
}
int main()
{
    poread(T);
    while (T--)
    {
        poread(n);
        printf("%d\n", sov((n + 1) / 2));
    }
}

T2购物

DP题解被ri了
\(Jackpei\) 喜欢购物,他尤其喜欢那种横扫一片商店的快感。最近,他打算对南门口的商店实行他疯狂的购物计划。南门口的商业区中最繁华的就是黄兴路步行街了。这条街上有\(n\)个商店,\(Jackpei\) 打算进攻\(m\)次,每次扫荡第\(L_i , R_i\)个商店,\(Jackpei\)会把他经过的每个商店扫荡一空(换句话说,就是一个商店不会被算两次),因为连续地扫一片商店是很爽的,所以 \(Jackpei\) 把一次扫荡的\(happy\)值定义为所有连续的一段没被扫空的商店\(happy\)值之和的平方的和,已被扫空的不再计算。
求扫荡的顺序以得到最大\(happy\)之和。
\(n \leq 5000 , m \leq 10^6\)


贪心策略:每次取当前的贡献最大的最大可行方案。因为之后该方案的贡献只减不增。
对每个\(i\)维护他能连续到的最远右端点即可。

int main()
{
    poread(n);
    poread(m);
    for (register int i = 1; i <= n; ++i)
        poread(a[i]), a[i] = a[i - 1] + a[i];
    for (register int i = 1, x, y; i <= m; ++i)
        poread(x), poread(y), poi[x] = max(poi[x], y);
    long long ans = 0;
    for (register int i = 1; i <= n; i)
    {
        register int sum = 0, st = 0, ed = 0;
        for (register int j = 1; j <= n; ++j)
            if (a[poi[j]] - a[j - 1] > sum && poi[j])
                sum = a[poi[j]] - a[j - 1], st = j;
        ed = poi[st];
        if (!sum)
        {
            printf("%lld", ans);
            return 0;
        }
        ans += (long long)sum * sum;
        for (register int j = 1; j <= st; ++j)
        {
            if (poi[j] >= st)
                poi[j] = st - 1;
        }
        for (register int j = st; j <= ed; ++j)
        {
            if (poi[j] > poi[ed + 1])
                poi[ed + 1] = poi[j];
            poi[j] = 0;
        }
    }
}

开long long... 自己把自己hack了

T3 宗教仪式

nofind 决定:给两个串 \(s1\) , \(s2\) ,对 \(s1\) 每个位置求可以失配小于等于 \(K\) 次的能否和 \(s2\) 匹配
正解是后缀数组


本来一上午加一中午用哈希 + 二分 把这题卡过去了
结果jackpei疯狂制造hack数据把我ri了。。。
就先放哈希 + 二分吧。学完正解后缀数组再来。

const int BS = 300010;
static char buf[BS];
short k;
int M = 97;
int T1, T2;
int p[200001];
int s1[200001], s2[100001];
inline int find(const int &x, const int &y)
{
    register int l = 0, r = min(T1 - x, T2 - y), res = 0, mid;
    while (l <= r)
    {
        mid = (l + r) >> 1;
        if (s1[x + mid] - s1[x] * p[mid] == s2[y + mid] - s2[y] * p[mid])
            res = mid, l = mid + 1;
        else
            r = mid - 1;
    }
    return res;
}
inline bool check(int x, int y)
{
    register int tmp;
    for (register short i = 1; i <= k; ++i)
    {
        tmp = find(x - 1, y - 1);
        x += tmp + 1, y += tmp + 1;
        if (y > T2)
            return true;
        //        cerr << x << " " << y << '\n';
    }
    tmp = find(x - 1, y - 1);
    x += tmp, y += tmp;
    return y > T2;
}
signed main()
{
    register char c = 0;
    register int *t1 = s1, *t2 = s2;
    register char *st, *ed;
    scanf("%s", buf + 1);
    T1 = strlen(buf + 1);
    st = buf + 1;
    for (register int *t1 = s1 + 1; t1 <= s1 + T1; ++t1, ++st)
    {
        *t1 = *(t1 - 1) * M + *st;
    }
    scanf("%s", buf + 1);
    T2 = strlen(buf + 1);
    st = buf + 1;
    for (register int *t2 = s2 + 1; t2 <= s2 + T2; ++t2, ++st)
    {
        *t2 = *(t2 - 1) * M + *st;
    }
    scanf("%d", &k);
    p[0] = 1;
    for (register unsigned i = 1; i <= T1; ++i)
    {
        p[i] = p[i - 1] * M;
    }
    register int t = T1 - T2 + 1;
    register int ans = 0;
    for (register int i = 1; i <= t; ++i)
    {
        ans += check(i, 1);
    }
    printf("%d", ans);
}

为了过这道题 去学了后缀数组没学会但学会了基数排序
这个
【模板】后缀排序

#include 
#include 
#include 
#define max(a, b) (a > b ? a : b)
#define min(a, b) (a < b ? a : b)
using namespace std;
const int BS = 300005;
const int MAXN = 300001;
char buf[BS];
int s[MAXN], a[MAXN], b[MAXN], st[20][MAXN], pre[MAXN], rak[MAXN], hei[MAXN], w[MAXN], sa[MAXN];
inline void pre_work(const int &n, int *x, int *y)
{
    register int m = 27;
    for (register int i = 0; i < n; ++i)
        ++w[x[i] = s[i]];
    for (register int i = 1; i < m; ++i)
        w[i] += w[i - 1];
    for (register int i = 0; i < n; ++i)
        sa[--w[x[i]]] = i;
    for (register int k = 1, p; k < n; k <<= 1)
    {
        p = 0;
        for (register int i = n - 1; i >= n - k; --i)
            y[p++] = i;
        for (register int i = 0; i < n; ++i)
            if (sa[i] >= k)
                y[p++] = sa[i] - k;
        for (register int i = 0; i < m; ++i)
            w[i] = 0;
        for (register int i = 0; i < n; ++i)
            ++w[x[y[i]]];
        for (register int i = 1; i < m; ++i)
            w[i] += w[i - 1];
        for (register int i = n - 1; i >= 0; --i)
            sa[--w[x[y[i]]]] = y[i];
        swap(x, y);
        x[sa[0]] = 0, p = 1;
        for (register int i = 1; i < n; ++i)
            x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? p - 1 : p++;
        m = p;
    }
    for (register int i = 0; i < n; ++i)
        rak[sa[i]] = i;
    m = 0;
    for (register int i = 0; i < n; hei[rak[i++]] = m)
    {
        if (!rak[i])
            continue;
        m = (m ? m - 1 : 0);
        for (register int j = sa[rak[i] - 1]; s[j + m] == s[i + m]; ++m)
            ;
    }
    for (register int i = 0; i < n; ++i)
        st[0][i] = hei[i];
    pre[1] = 0;
    register int t = log2(n) + 1;
    for (register int i = 2; i < n; ++i)
        pre[i] = ((i & (i - 1)) ? pre[i - 1] : pre[i - 1] + 1);
    for (register int i = 1; i < 20; ++i)
        for (register int j = 0; j < n; ++j)
            st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
}
inline int query(int l, int r)
{
    if (l > r)
        swap(l, r);
    int t = pre[r - l];
    return min(st[t][l + 1], st[t][r - (1 << t) + 1]);
}
int main()
{
    register int n, m, k = 0, ans = 0;
    register char *st;
    fread(st = buf, 1, BS, stdin);
    while (*st > 'z' || *st < 'a')
        ++st;
    register int *ss = s;
    do
    {
        *ss = *st - 'a';
        ++ss, ++st;
    } while (*st >= 'a' && *st <= 'z');
    *ss = 25;
    n = ss - s;
    ++ss;
    while (*st > 'z' || *st < 'a')
        ++st;
    do
    {
        *ss = *st - 'a';
        ++ss, ++st;
    } while (*st >= 'a' && *st <= 'z');
    m = ss - s - 1 - n;
    while (*st > '9' || *st < '0')
        ++st;
    do
    {
        k = k * 10 + *st - 48;
        ++st;
    } while (*st >= '0' && *st <= '9');
    pre_work(n + m + 1, a, b);
    for (register int i = 0; i + m <= n; ++i)
    {
        for (register int j = 0, p = i, q = 0; j <= k; j++, ++p, ++q)
        {
            if (q >= m)
            {
                ++ans;
                break;
            }
            register int t = query(rak[p], rak[n + 1 + q]);
            p += t, q += t;
            if (q >= m)
            {
                ++ans;
                break;
            }
        }
    }
    printf("%d", ans);
    return 0;
}

你可能感兴趣的:(2019.10.17模拟赛)