CF818E 纸牌游戏

1 CF818E 纸牌游戏

2 题目描述

时间限制 \(2s\) | 空间限制 \(256M\)

Vova again tries to play some computer card game.

The rules of deck creation in this game are simple. Vova is given an existing deck of \(n\) cards and a magic number \(k\). The order of the cards in the deck is fixed. Each card has a number written on it; number \(a_i\) is written on the i-th card in the deck.

After receiving the deck and the magic number, Vova removes \(x\) (possibly \(x = 0\)) cards from the top of the deck, \(y\) (possibly \(y = 0\)) cards from the bottom of the deck, and the rest of the deck is his new deck (Vova has to leave at least one card in the deck after removing cards). So Vova's new deck actually contains cards \(x + 1, x + 2, ... n - y - 1, n - y\) from the original deck.

Vova's new deck is considered valid iff the product of all numbers written on the cards in his new deck is divisible by \(k\). So Vova received a deck (possibly not a valid one) and a number \(k\), and now he wonders, how many ways are there to choose \(x\) and \(y\) so the deck he will get after removing \(x\) cards from the top and \(y\) cards from the bottom is valid?

数据范围:\(1 ≤ n ≤ 100000, 1 ≤ k ≤ 10^9\)

3 题解

容易想到,如果 \(k \mid n\),那么 \(k \mid n * m(m \in \mathbb{Z^+})\)。因此,我们发现:当我们选取的某个区间已经不能再缩小了(左或右边任意少乘一个数都会导致区间的积不是 \(k\) 的倍数),那么我们每往左或者右扩展一个数,可行的情况数就增加了 \(1\)。但是如果我们同时考虑左右端点,我们不容易计算出答案。因此我们可以考虑固定左端点,然后通过二分找到最左边的右端点 \(now\)。此时,我们增加的总情况数为 \(n - now + 1\)。由于我们遍历了所有可能的左端点,且每次的左端点不同,所以总共的答案既不会重复也不会遗漏。那么我们如何判断某个区间是否是 \(k\) 的倍数呢?显然,我们无法用前缀积:在除 \(0\) 时无法处理。所以我们选择使用线段树记录某一区间的积对 \(k\) 取模所得的值。如果某个区间的积对 \(k\) 取模的答案为 \(0\),那么该区间的积就是 \(k\) 的倍数。

最终我们的时间复杂度为 \(O(n \times log_2^2n)\),可以通过 \(10^5\)

4 代码(空格警告):

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1e5+10;
#define int long long
int n, k, ans, now;
int a[N];
struct node
{
    int l, r, sum;
}t[N*4];
void build(int p, int l, int r)
{
    t[p].l = l;
    t[p].r = r;
    if (l == r)
    {
        t[p].sum = a[l] % k;
        return ;
    }
    int mid = (l+r)/2;
    build(p*2, l, mid);
    build(p*2+1, mid+1, r);
    t[p].sum = (t[p*2].sum * t[p*2+1].sum) % k;
}
int query(int p, int l, int r)
{
    if (t[p].l >= l && t[p].r <= r) return t[p].sum;
    int mid = (t[p].l + t[p].r) / 2;
    int ans = 1;
    if (l <= mid) ans = (ans * query(p*2, l, r)) % k;
    if (r > mid) ans = (ans * query(p*2+1, l, r)) % k;
    return ans;
}
signed main()
{
    int l, r, mid;
    scanf("%lld %lld", &n, &k);
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    build(1, 1, n);
    for (int i = 1; i <= n; i++)
    {
        now = n+1;
        l = i;
        r = n;
        while (l <= r)
        {
            mid = (l+r)/2;
            if (!query(1, i, mid)) now = mid, r = mid-1;
            else l = mid+1;
        }
        ans += (n - now + 1);
    }
    cout << ans;
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-04-07 21:50  David24  阅读(72)  评论(0编辑  收藏  举报