牛客题 - Xieldy And His Password (找规律/想法/DP)

Xieldy And His Password (找规律/想法/DP)

题目链接 :西安电子科技大学第16届程序设计竞赛网络同步赛 -Xieldy And His Password


题意

众所周知,Xieldy最常用的口令是**
为了改变这一现状,他random了一个01串,并从中截取了一段作为自己的口令。
他选择的口令满足以下条件:

  1. 口令串表示的二进制数在十进制下可以被表示为3k(k>=0)。
  2. 口令串可以有前导零。

    现已经random出了01串,他想知道有多少种口令方案可以选择(不同的子段即为不同)。

输入描述:

若干组数据,每组数据仅一行01串s,表示random出来的的串,|s|<=1e6

输出描述:

输出口令的方案数。

思路一 (找规律+想法)

​ 既然题目的要求是要我们找一个01串二进制转化为十进制时能够被三整除,那么我们就找一找三的倍数二进制表示有什么规律。然后,我开始了漫漫长夜般探索,很遗憾,我什么都没发现,但是,比完赛后,有同学告诉了我他发现的规律,那就是一个二进制数如果要是三的倍数,那么它的奇数偶数位上的1一定相差3的倍数。

​ 0 11 110 1001 10101 这些例子都满足

那么我们就可以进行构思了,我构造了一个sum数组大小为三,分别记录了前面那一个奇偶差为0,1,2;的组合数。

举个例子

000000 的时候,初始sum[0] = 1,sum[1] = sum[2] = 0;

每次向左移动的过程中,计算当前的奇偶差,如果差为0,访问sum[0],与当前位有关的组合数为前一个奇偶差为0的数加一


代码一

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = (int)(j);i <= (int)(k);i ++)
#define per(i,j,k) for(int i = (int)(j);i >= (int)(k);i --)
#define mmm(a,b)   memset(a,b,sizeof(a))

typedef long long LL;
const int INF = (int)0x3f3f3f3f;
const LL MAXN = (LL)2e6+7;

char str[MAXN];
LL sum[3];
LL odd,eve;
LL ans;

void init(){
    mmm(sum,0);
    sum[0] = 1;
    ans = 0;
    odd = eve = 0;
}

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    while (cin >> str){
        init();
        LL len = strlen(str);
        rep(i,0,len-1){
            if (i&1) odd += str[i]-'0';
            else     eve += str[i]-'0';

            LL cha = ((odd-eve)%3+3)%3;//特别要注意这个操作,不能直接取绝对值,这样会破坏奇偶的差值
            ans += sum[cha];
            sum[cha] += 1;
        }
        cout << ans << endl;
    }
}

思路二 (DP)

​ 设每个二进制串s除以3有0,1,2三种转态(分别表示余数为0,余数为1,余数为2),那么根据推导有如下结论:

​ dp [i].[0] 以i位置结尾的 值%3余0的情况数。

​ dp [i].[1]以i位置结尾的 值%3余1的情况数。

​ dp [i].[2] 以i位置结尾的 值%3余2的情况数。

​ 当到达i位置时,i位置为1或0,有四种情况

​ 1 从前一个位置 值%3余0状态转移而来。

​ 2 从前一个位置 值%3余1状态转移而来。

​ 3 从前一个位置 值%3余2状态转移而来。

​ 4 从i位置,新开一种状态为1或0的情况。

​ 最后把位置 值%3余0 的状态的情况数加和,就是把所有以i位置结尾的能够被3整除的数加和。

​ 具体转移方程在代码里。

状态0 + ‘0’ ==》 状态0
状态1 + ‘0’ ==》 状态2
状态2 + ‘0’ ==》 状态1
状态0 + ‘1’ ==》 状态1
状态1 + ‘1’ ==》 状态0
状态2 + ‘1’ ==》 状态2


代码二

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = (int)(j);i <= (int)(k);i ++)
#define per(i,j,k) for(int i = (int)(j);i >= (int)(k);i --)
#define mmm(a,b)   memset(a,b,sizeof(a))

typedef long long ll;
const int INF = (int)0x3f3f3f3f;
const int MAXN = (int)1e6+7;

char str[MAXN];
ll dp[MAXN][3];
ll sum;

void init(){
    dp[0][0] = 0;
    dp[0][1] = dp[0][2] = -1;
    sum = 0;
}

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    while (cin >> str+1){
        init();
        ll len = strlen(str+1);
        rep(i,1,len){
            if (str[i] == '0'){
                dp[i][0] = dp[i-1][0]+1;
                dp[i][1] = dp[i-1][2];
                dp[i][2] = dp[i-1][1];
            }else {
                dp[i][0] = dp[i-1][1]+1;
                dp[i][1] = dp[i-1][0];
                dp[i][2] = dp[i-1][2];
            }
            sum += dp[i][0];
        }
        cout << sum << endl;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_41428565/article/details/80084459