2019牛客暑期多校训练营(第四场) - K - number - dp

https://ac.nowcoder.com/acm/contest/884/K

一开始整了好几个假算法,还好测了一下自己的样例过了。
考虑到300的倍数都是3的倍数+至少两个零(或者单独的0)。
求以第i个位置的数为结尾的前缀和为j的数的方案数。

当遇到至少两个0的时候,ans+=dp[0][i-2]+1。
+1是那两个0的贡献。

这样子算会漏算单独的0的贡献,最后加回去。

还因为忘记mod3段错误好几次。

老了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

char s[100005];
int a[100005];
int dp[3][100005];
int lx0[100005];
//ll ans[100005];

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    while(~scanf("%s", s)) {
        int n = strlen(s);
        for(int i = 0; i < n; ++i) {
            a[i] = s[i] - '0';
        }
        dp[0][0] = dp[1][0] = dp[2][0] = 0;
        dp[a[0] % 3][0]++;

        lx0[0] = (a[0] == 0);
        //ans[0] = 0;

        ll ans2 = 0;

        for(int i = 1; i < n; ++i) {
            dp[0][i] = dp[1][i] = dp[2][i] = 0;
            int c = a[i];
            dp[c % 3][i] = dp[0][i - 1];
            dp[(c + 1) % 3][i] = dp[1][i - 1];
            dp[(c + 2) % 3][i] = dp[2][i - 1];
            dp[c % 3][i]++;
            if(c == 0) {
                lx0[i] = lx0[i - 1] + 1;
            } else {
                lx0[i] = 0;
            }
            //ans[i] = ans[i - 1];
            if(lx0[i] >= 2) {
                //ans[i] += dp[0][i-2]+1;
                ans2 += dp[0][i-2]+1;
            }
        }

        for(int i = 0; i < n; ++i) {
            ans2 += (a[i] == 0);
        }

        /*for(int i = 0; i < n; i++) {
            printf("dp[%d][%d]=%d\n", 0, i, dp[0][i]);
            printf("dp[%d][%d]=%d\n", 1, i, dp[1][i]);
            printf("dp[%d][%d]=%d\n", 2, i, dp[2][i]);
            printf("ans[%d]=%lld\n", i, ans[i]);
            puts("");
        }*/

        printf("%lld\n", ans2);
    }
}

猜你喜欢

转载自www.cnblogs.com/Yinku/p/11256242.html