牛客练习赛13

A.幸运数字Ⅰ
定义一个数字为幸运数字当且仅当它的所有数位都是4或者7。
比如说,47、744、4都是幸运数字而5、17、467都不是。
现在,给定一个字符串s,请求出一个字符串,使得:
1、它所代表的整数是一个幸运数字;
2、它非空;
3、它作为s的子串(不是子序列)出现了最多的次数(不能为0次)。
请求出这个串(如果有多解,请输出字典序最小的那一个)。

这题做复杂了,其实只用判断是否4和7的个数就行了

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define CLR(a) memset(a, 0, sizeof(a))
#define DBG(x) cout<<(#x)<<"="<<x<<endl
#define FOR(i, a, b) for(int i=(a); i<(b); i++)
#define REP(i, a, b) for(int i=(a); i<=(b); i++)
#define DOWN(i, a, b) for(int i=(a); i>=(b); i--)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000009;
const int N= 1e5 +10;

string str, s, ans;
int tmp;
map<string, int> m;

bool judge() {
    int len=s.length();
    FOR(i, 0, len) {
        if (s[i]!='4'&&s[i]!='7') return false;
    }
    return true;
}

int main() {
    cin>>str;
    int len=str.length();
    FOR(i, 0, len) {
        FOR(j, i, len) {
            s="";
            REP(k, i, j) s+=str[k]; 
            if (judge()) {
                m[s]++;
                if (m[s]>tmp||m[s]==tmp&&s<ans) {
                    tmp=m[s];
                    ans=s;
                }
            }
        }
    }
    if (!tmp) cout<<-1<<endl;
    else cout<<ans<<endl;
    //cout<<1.*clock()/CLOCKS_PER_SEC<<"ms"<<"\n";
    return 0;
}

B.幸运数字Ⅱ
定义next(x)为大于等于x的第一个幸运数字。给定l,r,请求出next(l) + next(l + 1) + … + next(r – 1) + next(r)。

简单想一下,就会发现连续的next(i)是一样,所以我们只用预处理一下,再求一下分界点,分段求一下和就好了。

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define CLR(a) memset(a, 0, sizeof(a))
#define DBG(x) cout<<(#x)<<"="<<x<<endl
#define FOR(i, a, b) for(int i=(a); i<(b); i++)
#define REP(i, a, b) for(int i=(a); i<=(b); i++)
#define DOWN(i, a, b) for(int i=(a); i>=(b); i--)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000009;
const int N= 1e5 +10;

ll l, r;
vector<ll> a;
ll sum[N];

void init() {
    a.pb(0);
    a.pb(4);
    a.pb(7);
    int st=1, ed=2;
    REP(i, 2, 10) {
        ll tmp1=4;
        ll tmp2=7;
        REP(j, 1, i-1) tmp1*=10;
        REP(j, 1, i-1) tmp2*=10;
        REP(j, st, ed) a.pb(tmp1+a[j]);
        REP(j, st, ed) a.pb(tmp2+a[j]);
        st=ed+1;
        ed+=(1<<i);
    }
    sum[1]=16;
    FOR(i, 2, a.size()) {
        // DBG(a[i]);
        sum[i]=sum[i-1]+(a[i]-a[i-1])*a[i];
        // DBG(sum[i]);
    }
}

ll solve(ll n) {
    if (!n) return 0;
    int pos=lower_bound(a.begin(), a.end(), n) - a.begin() ;
    return sum[pos-1]+(n-a[pos-1])*a[pos]; 
}


int main() {
    init();
    cin>>l>>r;
    cout<<solve(r)-solve(l-1)<<endl;
    //cout<<1.*clock()/CLOCKS_PER_SEC<<"ms"<<"\n";
    return 0;
}

C.幸运数字Ⅲ
假设现在有一个数字d,现在想在d上重复k次操作。
假设d有n位,用d1,d2,…,dn表示。
对于每次操作,我们想要找到最小的x (x < n),使得dx=4并且dx+1=7。
如果x为奇数,那么我们把dx和dx+1都变成4;
否则,如果x为偶数,我们把dx和dx+1都变成7;
如果不存在x,那么我们不做任何修改。
现在请问k次操作以后,d会变成什么样子。

简单模拟一下就会发现,477会不断循环。

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define CLR(a) memset(a, 0, sizeof(a))
#define DBG(x) cout<<(#x)<<"="<<x<<endl
#define FOR(i, a, b) for(int i=(a); i<(b); i++)
#define REP(i, a, b) for(int i=(a); i<=(b); i++)
#define DOWN(i, a, b) for(int i=(a); i>=(b); i--)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000009;
const int N= 1e5 +10;

char a[N];
int pos, flag;
ll n, k;

int main() {
    cin>>n>>k;
    scanf("%s",a+1);
    pos=1;
    REP(i, 1, k) {
        flag=0;
        while(pos<n) {
            if (a[pos]=='4'&&a[pos+1]=='7') {
                flag=1;
                break;
            }
            pos++;
        }
        if (flag) {
            if (pos%2==1) {
                a[pos+1]='4';
            }
            else {
                a[pos]='7';
                if (pos>1&&a[pos-1]=='4') {
                    if ((k-i)%2==1) a[pos]='4';
                    else a[pos]='7';
                    break;
                }
            }
        }
        else break;
    }
    printf("%s\n",a+1);
    //cout<<1.*clock()/CLOCKS_PER_SEC<<"ms"<<"\n";
    return 0;
}

D.幸运数字Ⅳ
现在想知道在1…n的第k小的排列中,有多少个幸运数字所在的位置的序号也是幸运数字。

这道题应该是最好的一道题,首先要复习一下康托展开和逆展开,然后还要处理n太大的情况,当n很大时,排列前面的数都是从1开始按顺序排列,而且我们不能把排列存下来,也不需要存下来,因为排列开头的数,序号和对应序号的数是一样的,预处理一下幸运数字记录一下就好了。

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define CLR(a) memset(a, 0, sizeof(a))
#define DBG(x) cout<<(#x)<<"="<<x<<endl
#define FOR(i, a, b) for(int i=(a); i<(b); i++)
#define REP(i, a, b) for(int i=(a); i<=(b); i++)
#define DOWN(i, a, b) for(int i=(a); i>=(b); i--)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000009;
const int N= 1e5 +10;

int n, k, t;
ll f[N];
bool vis[N];
int ans[N];
int cnt;
map<ll, int> MP;

void init() {
    f[0]=1;
    REP(i, 1, 20) f[i]=f[i-1]*i;
}

bool check(ll n) {
    int t;
    while(n>0) {
        t=n%10;
        if (t!=4&&t!=7) return false;
        n/=10;
    }
    return true;
}

void dfs(ll n) {
    if (n>1e9) return;
    if (n) MP[n]=1;
    dfs(n*10+4);
    dfs(n*10+7);
}

int main() {
    init();
    dfs(0);
    int ans2=0;
    cin>>n>>k;
     // REP(i, 1, 30) DBG(f[i]);
    int m=min(15, n);
    DOWN(i, m, 1) {
        t=(k-1)/f[i-1]+1;
        k-=(t-1)*f[i-1];
        cnt=0;
        REP(j, 1, m) {
            if (!vis[j]) cnt++;
            if (cnt==t) {
                if (MP[n+1-i]&&MP[n-m+j]) ans2++; 
                vis[j]=true;
                break;
            }
        }
    }

    // REP(i, 1, n) cout<<ans[i]<<' ';
    // cout<<endl;
    if (n>m) {
        for(auto it=MP.begin();it!=MP.end();it++){
            if ((*it).first>0&&(*it).first<=n-m) ans2+=(*it).second;
        }
    }
    cout<<ans2<<endl;
    //cout<<1.*clock()/CLOCKS_PER_SEC<<"ms"<<"\n";
    return 0;
}

E.乌龟跑步
有一只乌龟,初始在0的位置向右跑。
这只乌龟会依次接到一串指令,指令T表示向后转,指令F表示向前移动一个单位。乌龟不能忽视任何指令。
现在我们要修改其中正好n个指令(一个指令可以被改多次,一次修改定义为把某一个T变成F或把某一个F变成T)。
求这只乌龟在结束的时候离起点的最远距离。(假设乌龟最后的位置为x,我们想要abs(x)最大,输出最大的abs(x))

动态规划,dp[i][j][0]和dp[i][j][1],表示前i个指令,修改了j次,分别面向右边和左边时,最右的位置,相应的还有dp2表示最左的位置。
很关键的一个技巧,状态转移取最大值时,用-1或-INF代表不合法状态;状态转移取最小值时,用INF代表不合法状态,很有用。

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define CLR(a) memset(a, 0, sizeof(a))
#define DBG(x) cout<<(#x)<<"="<<x<<endl
#define FOR(i, a, b) for(int i=(a); i<(b); i++)
#define REP(i, a, b) for(int i=(a); i<=(b); i++)
#define DOWN(i, a, b) for(int i=(a); i>=(b); i--)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000009;
const int N= 1e2 +10;

int dp[N][N][2];
int dp2[N][N][2];
int t, n;

void go(int i, int j, int k) {
    if (dp[i-1][k][1]!=-INF) dp[i][j][1]=max(dp[i-1][k][1]+1,dp[i][j][1]);
    if (dp[i-1][k][0]!=-INF) dp[i][j][0]=max(dp[i-1][k][0]-1,dp[i][j][0]);
}
void turn(int i, int j, int k) {
    if (dp[i-1][k][1]!=-INF) dp[i][j][0]=max(dp[i-1][k][1],dp[i][j][0]);
    if (dp[i-1][k][0]!=-INF) dp[i][j][1]=max(dp[i-1][k][0],dp[i][j][1]);
}


void go2(int i,int j, int k) {
    if (dp2[i-1][k][0]!=INF) dp2[i][j][0]=min(dp2[i-1][k][0]-1,dp2[i][j][0]);
    if (dp2[i-1][k][1]!=INF) dp2[i][j][1]=min(dp2[i-1][k][1]+1,dp2[i][j][1]);
}
void turn2(int i, int j, int k) {
    if (dp2[i-1][k][1]!=INF) dp2[i][j][0]=min(dp2[i-1][k][1],dp2[i][j][0]);
    if (dp2[i-1][k][0]!=INF) dp2[i][j][1]=min(dp2[i-1][k][0],dp2[i][j][1]);
}


int main() {
    char a[N];
    scanf("%s",a+1);
    cin>>n;
    int len=strlen(a+1);

    REP(i, 0, len) {
        REP(j, 0, n) {
            REP(k, 0, 1) {
                dp[i][j][k]=-INF;
            }
        }
    }
    memset(dp2, 0x3f, sizeof(dp2));
    dp[0][0][1]=0;
    dp2[0][0][1]=0;

    REP(i, 1, len) {
        REP(j, 0, n) {
            REP(k, 0, j) {
                t=j-k;
                if (t%2==1) {
                    if (a[i]=='T') go(i, j, k);
                    else turn(i, j, k);
                }
                else {
                    if (a[i]=='T') turn(i, j, k);
                    else go(i, j, k);
                }
            }
            // DBG(dp[i][j][1]);
            // DBG(dp[i][j][0]);
            // DBG(i);
            // DBG(j);
        }
    }


    REP(i, 1, len) {
        REP(j, 0, n) {
            REP(k, 0, j) {
                t=j-k;
                if (t%2==1) {
                    if (a[i]=='T') go2(i, j ,k);
                    else turn2(i, j, k);
                }
                else {
                    if (a[i]=='T') turn2(i, j, k);
                    else go2(i, j, k);
                }
            }   
            // DBG(dp2[i][j][1]);
            // DBG(dp2[i][j][0]);
            // DBG(i);
            // DBG(j);
        }
    }


    int ans=-INF;
    if (dp[len][n][0]!=-INF) ans=max(abs(dp[len][n][0]), ans);
    if (dp[len][n][1]!=-INF) ans=max(abs(dp[len][n][1]), ans);
    if (dp2[len][n][0]!=INF) ans=max(abs(dp2[len][n][0]), ans);
    if (dp2[len][n][1]!=INF) ans=max(abs(dp2[len][n][1]), ans);
    cout<<ans<<endl;
    //cout<<1.*clock()/CLOCKS_PER_SEC<<"ms"<<"\n";
    return 0;
}

F.m皇后
在一个n*n的国际象棋棋盘上有m个皇后。
一个皇后可以攻击其他八个方向的皇后(上、下、左、右、左上、右上、左下、右下)。
对于某个皇后,如果某一个方向上有其他皇后,那么这个方向对她就是不安全的。
对于每个皇后,我们都能知道她在几个方向上是不安全的。

现在我们想要求出t0,t1,…,t8,其中ti表示恰有i个方向是”不安全的”的皇后有多少个。

这题只要知道那八种方向如何表示,然后记录最大值和最小值,只要小于最大值,最说明”下”不安全;只要大于最小值,就说明”上”不安全,这里上下可以换成其他的相应的方向,这里做的时候傻了,正着扫了一遍,反着有扫了一遍,其实只用扫一遍就可以了。

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define CLR(a) memset(a, 0, sizeof(a))
#define DBG(x) cout<<(#x)<<"="<<x<<endl
#define FOR(i, a, b) for(int i=(a); i<(b); i++)
#define REP(i, a, b) for(int i=(a); i<=(b); i++)
#define DOWN(i, a, b) for(int i=(a); i>=(b); i--)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000009;
const int N= 2e6 +10;

int Cmax[N], Cmin[N];
int Rmax[N], Rmin[N];
int D1max[N], D1min[N];
int D2max[N], D2min[N];
int ok[N][8];
int ans[8];
int r[N], c[N];
int row, col;
int cnt;

int n, m;

void init() {
    REP(i, 1, 2*n) {
        Cmax[i]=0, Cmin[i]=INF;
        Rmax[i]=0, Rmin[i]=INF;
        D1max[i]=0, D1min[i]=INF;
        D2max[i]=0, D2min[i]=INF;
    }
}

int main() {
    scanf("%d %d", &n, &m);
    REP(i, 1, m) scanf("%d%d", &r[i], &c[i]);
    init(); 
    REP(i, 1, m) {
        row=r[i];
        col=c[i];
        if (row<Cmax[col]) ok[i][0]=1;
        if (row>Cmin[col]) ok[i][1]=1;
        if (col<Rmax[row]) ok[i][2]=1;
        if (col>Rmin[row]) ok[i][3]=1;
        if (row<D1max[row-col+n]) ok[i][4]=1;
        if (row>D1min[row-col+n]) ok[i][5]=1;
        if (row<D2max[row+col-1]) ok[i][6]=1;
        if (row>D2min[row+col-1]) ok[i][7]=1;

        Cmax[col]=max(Cmax[col],row);//下
        Cmin[col]=min(Cmin[col],row);//上
        Rmax[row]=max(Rmax[row],col);//右
        Rmin[row]=min(Rmin[row],col);//左
        D1max[row-col+n]=max(D1max[row-col+n],row);//右下
        D1min[row-col+n]=min(D1min[row-col+n],row);//左上
        D2max[row+col-1]=max(D2max[row+col-1],row);//左下
        D2min[row+col-1]=min(D2min[row+col-1],row);//右上

    }

    init(); 
    DOWN(i, m, 1) {
        row=r[i];
        col=c[i];
        if (row<Cmax[col]) ok[i][0]=1;
        if (row>Cmin[col]) ok[i][1]=1;
        if (col<Rmax[row]) ok[i][2]=1;
        if (col>Rmin[row]) ok[i][3]=1;
        if (row<D1max[row-col+n]) ok[i][4]=1;
        if (row>D1min[row-col+n]) ok[i][5]=1;
        if (row<D2max[row+col-1]) ok[i][6]=1;
        if (row>D2min[row+col-1]) ok[i][7]=1;

        Cmax[col]=max(Cmax[col],row);//下
        Cmin[col]=min(Cmin[col],row);//上
        Rmax[row]=max(Rmax[row],col);//右
        Rmin[row]=min(Rmin[row],col);//左
        D1max[row-col+n]=max(D1max[row-col+n],row);//右下
        D1min[row-col+n]=min(D1min[row-col+n],row);//左上
        D2max[row+col-1]=max(D2max[row+col-1],row);//左下
        D2min[row+col-1]=min(D2min[row+col-1],row);//右上

    }

    REP(i, 1, m) {
        cnt=0;
        FOR(j, 0, 8) {
            // cout<<ok[i][j]<<' ';
            if (ok[i][j]) cnt++;
        }
        // cout<<endl;
        ans[cnt]++;
    }

    REP(i, 0, 7) cout<<ans[i]<<' ';
    cout<<ans[8]<<endl;
    //cout<<1.*clock()/CLOCKS_PER_SEC<<"ms"<<"\n";
    return 0;
}
点赞