第十三届NENUACM校赛正式赛总结

目录

A

B

C

D

E

G

H

I


这次比赛主要是心态稳定,一直坚持不懈地想把题A掉,运气好就好在有很多思维题,我出的题没有用到高级点的算法。

一个点在于自己潜心研究了一点组合数学,其实并没有比以前强只是有思考问题的自信了。

这是我参加的第一次校赛也是最后一次校赛,感谢出题的聚聚手下留情让我水了一波一等,学长们为了降低难度也是操碎了心。

A

A - 拆魔方达人

Time Limit: 1000/1000MS (C++/Others) Memory Limit: 262144/262144KB (C++/Others)

Problem Description

喵帕斯从小就喜欢玩魔方,最近他的魔方瘾又来了,于是在网上买了沃耶布支岛上所有种类的魔方来玩。在玩魔方的时候,喵帕斯不小心把一个4*4*4大小的魔方弄散架了,他费了很大力气才把魔方重新拼好,然而就在这时他的脑海中产生了一个问题:一个n*n*n的魔方究竟可以拆出多少个至少有一面涂有颜色的小块呢(魔方的内部默认是没有颜色的)?
已知沃耶布支岛上不同大小的正方体魔方有如下规律:一个2*2*2的魔方可以拆出2*2*2-0*0*0=8个至少一面涂有颜色的小块,一个3*3*3的魔方可以拆出3*3*3-1*1*1=26个带有颜色的小块。以此类推,一个4*4*4的魔方可以拆出56个带有颜色的小块。一个5*5*5的魔方则可以拆出98个带有颜色的小块。那么聪明的你能帮喵帕斯算算一个已确定棱长的正方体魔方能拆出多少个至少有一面涂有颜色的小块吗?

Input

输入数据首先包括一个整数n(1≤n≤100),表示测试实例的个数。接下来n行每行表示一个测试实例,其中包括一个整数k(2≤k≤1e8),表示正方体魔方的棱长。

Output

对于每个测试实例,输出正方体魔方可拆出的零件数,每个实例的输出占一行。

Sample Input

1
3

Sample Output

26

 思路:很简单,没有着色的内部的块的长宽高一定是魔方块的长宽高-2,水过

/**
* this code is made by C_19_25
* Problem: 489
* Result: Accepted
* Submission Date: 2019-04-14 09:32:55
* Time: 0ms
* Memory: 584KB
*/
#include <bits/stdc++.h>
#define eps 1e-8
using namespace std;
typedef long long ll;
const int maxn = 1e6;
const int INF = 1e9;
const ll linf = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
ll mod = 1e9+7;
ll a[maxn];


int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        ll n;
        cin>>n;
        ll ans = (n*n*n)-(n-2)*(n-2)*(n-2);
        cout<<ans<<endl;

    }


    return 0;
}

B

B - 长虎快乐数

Time Limit: 1000/1000MS (C++/Others) Memory Limit: 262144/262144KB (C++/Others)

Problem Description

长虎在钻研深奥的数学,他把能够带给他快乐的数称作长虎快乐数。已知一个数能够带给长虎快乐当且仅当它满足下列的条件:

  1. 它的数字只包含0, 1, 2,且0, 1, 2这三个数字都出现过至少一次。

  2. 所有的0都出现在所有的1之前。

  3. 最高位数字不为0。

因此,符合我们定义的最小的快乐数是201。除此之外,2011, 2201也是长虎快乐数。

请计算恰好有n位的快乐数的个数。由于答案可能非常大,只需要输出答案除以1000000007的余数。

Input

输入第一行包括一个正整数T(1≤T≤10),表示测试实例的个数。

接下来T行每行包括一个正整数n (3≤n≤10000000),表示快乐数的位数。

Output

对于每个测试实例,输出恰好n位的整数中快乐数的个数除以1000000007的余数。

Sample Input

1
3

Sample Output

1

这个题当时做了很久很久很久,交了18次也是没谁了,感觉这个题提交次数的一半都是我贡献的。

思路:首先,由于0不能放在最前, 所有1都要在0后,那么第1位一定是2.

考虑最低位的0,计为第i位,最高位的1,记为第j位,则1<i<j<=n,由于我们限定了第i位为最低位0,第j位为最高位1,那么i与j之间的位只能填2,对于1<k1<i的第k1位,可以填0或2,对于j<k2<=n的第k2位,可以填0或1.

考虑从第i位起到第j位的这个数串,假设它的长度为L,则L可以取2~n-1,对应地,这个数串的放法就有n-2~1种。

对于长度为L的数串,剩余可填(0,1)或(0,2)的数位就有n-L-1位,有2^(n-L-1)种

所以此题答案就是\sum_{i=1}^{n-2}i*2^{i-1}

/**
* this code is made by C_19_25
* Problem: 490
* Result: Accepted
* Submission Date: 2019-04-14 13:21:59
* Time: 728ms
* Memory: 2768KB
*/
#include <bits/stdc++.h>
#define eps 1e-8
using namespace std;
typedef long long ll;
const int maxn = 1e8;
const int INF = 1e9;
const ll linf = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
ll mod = 1000000007;
ll a[maxn];
int main()
{
    a[0] = 0;
    ll pow = 1;
    for(int i = 1; i<=1e7; i++)
    {
        ll cur = ((i%mod)*(pow%mod))%mod;
        a[i] = ((a[i-1]%mod)+ (cur%mod))%mod;
        pow *= 2;
        pow %= mod;
    }

    int T;
    cin>>T;
    while(T--)
    {
        ll n;
        cin>>n;
        if(n<3)
        {
            cout<<0<<endl;
            continue;
        }
        cout<<a[n-2]<<endl;
    }


    return 0;
}

 

C

C - 长虎的期望

Time Limit: 1000/1000MS (C++/Others) Memory Limit: 262144/262144KB (C++/Others)

Problem Description

某天长虎在研究图论。在图论中,一个包含n个结点的有向图的割指的是将n个结点划分成两个点集合S和S1时,从S连向S1的边的条数。
例如:

 

当有向图如上图所示,且S={0},S1={1,2}时,有向图的割为2。
已知S和S1满足如下性质:
1.S可能为空集,也可能包含全部的n个结点。
2.S1为S在有向图上的补集。
现在长虎给你一个包含n个结点的有向图,n个结点的序号分别为0,1...n-1,并请你回答:将n个结点任意划分为S和S1两个集合,割的期望是多少。

Input

输入第一行包括一个正整数T(1≤T≤100),表示测试样例的个数。

每组输入包括(m+1)行,第一行输入两个整数n,m(1≤n≤100,0≤m≤1000),代表给出的有向图包含n个结点,m条有向边。
接下来m行,每行输入两个整数u,v(0≤u,v≤100),代表有向图中存在一条从序号为u的结点连向序号为v的结点的有向边。题目数据保证输入数据的有向图中不包含自环。

Output

对于每组输入实例,输出仅有一行,输出割的期望,若期望为整数则直接输出整数,若期望为分数,则先将分数约分化简至最简形式,再以“分子/分母”形式输出。例如当期望为2时,应当输出2;当期望为0.75时,应当输出3/4。

Sample Input

1
3 2
0 1
0 2

Sample Output

1/2

思路:考虑边i->j,只有i在S中且j在S1种,这条边才能计入割。共有n个点,已经确定了2个点的去向,还剩n-2个点,所以此边为割的情况共有2^(n-2)种,每一条边都是这样,那么总割数加起来一共是m*2^(n-2),又因为一共有2^n种情况,二者相除,期望为m/4,化简一下就完事。

/**
* this code is made by C_19_25
* Problem: 491
* Result: Accepted
* Submission Date: 2019-04-14 12:14:08
* Time: 12ms
* Memory: 596KB
*/
#include <bits/stdc++.h>
#define eps 1e-8
typedef long long ll;
using namespace std;
const int maxn = 1e6;
const int INF = 1e9;
const ll linf = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
int gcd(int x, int y)
{
    if(x<y)
        swap(x,y);
    if(y == 0)
        return x;
    return gcd(y,x%y);
}

int from[maxn],to[maxn];
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,m;
        cin>>n>>m;
        for(int i = 0; i<m; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            from[i] = x;
            from[y] = y;
        }
        int d = gcd(m,4);
        m /= d;
        int mu = 4/d;
        if(m%mu)
        {
            cout<<m<<"/"<<mu<<endl;
        }
        else
            cout<<m/mu<<endl;


    }



    return 0;
}

D

D - 拍张合照吧

Time Limit: 1000/1000MS (C++/Others) Memory Limit: 262144/262144KB (C++/Others)

Problem Description

ACM集训队要拍合照啦!

如图一所示,刚开始队员们都随机地站在某个台阶上,现在两位队长,WC和SGH的任务是让队员们在台阶上排成一列以显得美观。WC和SGH决定利用这个机会玩一个游戏。游戏规则如下:

WC和SGH分别指挥某位队员向下走任意多级的台阶(至少一级),但不能越过低级台阶上的队员,也不能走下最低级台阶(图中黑色台阶)。且两位队员不能站在同一台阶上。

WC和SGH轮流发出指令,最后所有的队员必然会就位在低段台阶,再也不能向下移动(如图二所示)。轮到谁指挥时无法继续移动,则游戏结束,该队长认输。

假定WC和SGH都是玩这个游戏的高手,他们一定会采取最优的游戏策略。现在你已知队员们的初始位置,以及WC先手操作,请你计算在当前情况下,WC和SGH谁会胜出。

          

图一                                                                        图二

Input

输入第一行包括一个正整数T(1≤T≤100),表示测试样例的个数。

每组测试样例包括两行,第一行输入一个正整数N,代表队员的人数。

第二行包括用空格分开的N个整数a1,a2,...,aN,ai表示队员的位置。输入数据保证不会出现两位队员站在同一台阶上。台阶序号从1算起,最低级台阶即为第一级台阶。(1≤N≤100, 1≤ai≤1000)

Output

对于每组测试样例,输出仅有一行,若WC先手获胜则输出”WC”,否则输出”SGH”。

Sample Input

2
3
1 2 3
3
1 3 4

Sample Output

SGH
SGH

Hint

第一组样例中,WC先手,但是队员已经无法再向下移动,因此SGH获胜

第二组样例中,WC先手并只能让站在第三级台阶的队员移下一级,SGH接着让站在第四级台阶的队员移下一级,此时轮到WC但队员已经无法向下移动,因此SGH获胜

还没想好,先粘一下正解。

/**
* this code is made by C_19_51
* Problem: 492
* Result: Accepted
* Submission Date: 2019-04-14 11:51:25
* Time: 0ms
* Memory: 584KB
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
#include<set>
const int MAX_N=1000;
int N,P[MAX_N];
const double pi=acos(-1.0);
using namespace std;
void solve()
{
    if(N%2==1) P[N++]=0;
    sort(P,P+N);

    int x=0;
    for(int i =0;i+1<N;i+=2)
    {
        x ^=(P[i+1]-P[i]-1);
    }
    if(x==0) puts("SGH");
    else puts("WC");
}
int main()
{
 int n;
 cin>>n;
 while(n--)
 {
     cin>>N;
     for(int i =0;i<N;i++)
     {
         cin>>P[i];
     }
     solve();
 }

}

 E

E - 动态规划

Time Limit: 1000/1000MS (C++/Others) Memory Limit: 262144/262144KB (C++/Others)

Problem Description

第十三届东北师范大学程序设计竞赛热身赛结束了!

喵帕斯看完了热身赛的榜单:你们这些出题人还是太仁慈了,看看我去年出的题目,选手们都是奔着爆零去的。

WC:信你个鬼,不满意题目难度就自己出题啊。

喵帕斯:我退役了┗( ▔, ▔ )┛我不想出就不出,你咬我啊。

WC:这个老头坏得很...

思前想后,WC做出了一个艰难的决定:对题目难度做一个动态的规划,适当增加一下比赛题目的难度。现在已知本届校赛正式赛共有11道题,每道题都有自己的难度系数,且第i题的难度系数记为a_i。本届校赛的难度系数为11道题的难度系数之和。WC决定将11道题中最简单的题目(即难度系数最低的题目)的难度系数调高,并且直接将难度系数调高至与最难的题目的难度系数相等。

WC在调整难度的时候,发现难度系数最低的题目可能不止一道。即有一些题目难度系数相等,且并列难度系数最低。他稍加思索便决定把所有的这些题目全部调整难度(喵帕斯:WC这个老头坏得很)。请你求出在WC调整难度之后,本届校赛的难度系数是多少。

Input

输入的第一行包括一个正整数T(1≤T≤100),表示测试样例的组数。

每组测试样例包括一行,包括十一个正整数a_1,a_2,...a_11,中间用空格隔开,表示调整难度前十一道题的难度系数。(1≤a_i≤1e9)

Output

对于每组测试样例,输出包含一行,输出在WC调整难度后,本届校赛的难度系数是多少。

Sample Input

1
1 1 1 1 1 1 1 1 1 1 2

Sample Output

22

 只有名字很难的题,不多赘述。

/**
* this code is made by C_19_25
* Problem: 493
* Result: Accepted
* Submission Date: 2019-04-14 09:29:59
* Time: 0ms
* Memory: 596KB
*/
#include <bits/stdc++.h>
#define eps 1e-8
using namespace std;
typedef long long ll;
const int maxn = 1e6;
const int INF = 1e9;
const ll linf = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
ll mod = 1e9+7;
ll a[maxn];


int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        //cin>>n;
        ll ans = 0;
        int minx = INF;
        int maxx = 0;
        for(int i = 0; i<11; i++)
        {
            int x;
            scanf("%d",&x);
            a[i] = x;
            minx  = min(minx,x);
            maxx = max(maxx,x);
        }
        for(int i = 0; i<11; i++)
        {
            if(a[i] == minx)
                ans += maxx;
            else
                ans += a[i];
        }
        cout<<ans<<endl;
    }


    return 0;
}

F - 路径计数

Time Limit: 1000/1000MS (C++/Others) Memory Limit: 262144/262144KB (C++/Others)

Problem Description

长虎最近对计数特别感兴趣,于是他考虑了这么一个问题:给定m,n,p,q四个正整数,且p<m,q<n,在平面直角坐标系内取定四个点A(0,0),B(p,0),C(m,q),D(m,n)。考虑从点A到点D的路径f和从点B到点C的路径g,f和g只能沿着坐标轴的正方向且只能在整点处改变方向,(也就是只能向右向上走),且f,g没有公共点。问满足条件的(f,g)路径序对的个数对1000000007取余数的结果是多少。

Input

输入第一行包括一个正整数T(1≤T≤100),表示测试样例的组数。

每组测试样例输入包括一行,输入四个正整数m,n,p,q(1≤p<m≤1e7,1≤q<n≤1e7)。

Output

每组测试样例输出占一行,输出路径序对的个数模1e9+7。

Sample Input

1
2 2 1 1

Sample Output

3

思路:总路径对数-有公共点的路径对数

对于路径对(f,g),可以把A->D B->C这两条路径,在第一个公共点进行转化,变成A->C B->D的路径。

而这种有公共点的A->D B->C的路径对,和上述的A->C B->D的路径对一一对应。

故次题的答案为\binom{n+m}{n}*\binom{m-p+q}{q}-\binom{m+q}{m}*\binom{m-p+n}{n}

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <map>
#include <vector>
#include <queue>
#include <set>
#define eps 1e-8
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 1e8;
const int INF = 1e9;
const ll linf = 0x3f3f3f3f3f3f3f3f;
using namespace std;
ll P[maxn];
ll mod = 1e9+7;
void init()
{
    P[0] = 1;
    for(int i = 1; i<=2e7; i++)
    {
        P[i] = ((P[i-1]%mod)*(i%mod))%mod;
        //inv[i] = modReverse(P[i]);
    }
}
ll fast(ll a, ll n)
{
    if(n == 0)
        return 1;
    if(n&1)
        return a*fast(a,n-1)%mod;
    ll tmp = fast(a,n>>1);
    return tmp*tmp%mod;
}
ll Com(ll n, ll m)
{
    if(n == m || m == 0)
        return 1;
    return ((P[n]*fast(P[m],mod-2)%mod)*fast(P[n-m],mod-2))%mod;
}
int main()
{
    init();
    int T;
    cin>>T;
    while(T--)
    {
        ll m,n,p,q;
        cin>>m>>n>>p>>q;
        ll ans1 = Com(m+n,m)*Com(m-p+q,q)%mod;
        ll ans2 = Com(m+q,m)*Com(m-p+n,n)%mod;
        ll ans = (ans1-ans2+mod)%mod;
        cout<<ans<<endl;
    }


    return 0;
}

 

 

G

G - 排队

Time Limit: 1000/1000MS (C++/Others) Memory Limit: 262144/262144KB (C++/Others)

Problem Description

SGH寒假的时候请阿z喝奶茶,阿z迟到了而SGH在一点点等她。这个时候一点点已经有了n个人在排队,而在等阿z的过程中,还不断得有新来的人,排在了SGH的前面(SGH不知道阿z想喝什么,怕挨打只好一直排在队尾,哦,可怜的SGH )。 SGH 在等待的过程中,会时不时的和阿z通话,阿z会询问SGH什么时候才轮到他,因为SGH一直在队尾的原因,所以这个时间其实就是除他以外所有人需要的时间。

已知会有人来,会有人走,请在每一次阿z询问的时候,告诉她SGH​还要排多久

你以为这样就结束了吗SGH怎么会甘心做一只报时的舔狗,他会时不时得利用超能力,回到若干次询问之前的状态,而因为他改变了时间线,所以有可能之后的一切都发生了改变!(邪恶的样例会告诉大家这个邪恶的能力是什么)

Input

第一行有两个整数 n, q 分别表示一开始排队的人数n,以及之后的q次操作  1 <= n, q <= 100000. 

第二行有n​个数 a_1 .... a_n​,分别表示 第 i​ 个人所需要花费的时间 1 <= a_i <= 1000000000​. 

之后有q行,每行表示一个操作 

Ins x 表示在队尾排入一个花费时间为 x 的人 
Out x 表示从队首有 x 个人离开
Query 表示查询当前时刻,正在排队的人所用的总时间是多少(不考虑队首那个人的奶茶已经做了多久)
Back x 表示时间回到 x 次操作之前(时间回溯表示在回溯的x次操作当中,离开的人又回来了,而这 x 次操作中新来排队的人好似从来没有出现过,之后会不会排队,不知道,题目保证时间回溯合法)

Output

对于每次的 Query 步骤,输出一行,该行只有一个数字,表示当前时刻正在排队的人所需要的总时间。

Sample Input

3 5
1 2 3
Ins 4
Out 2
Query
Back 2
Query

Sample Output

7
10

思路:前缀和解决,模拟一下队列的队头和队尾就可以了。

 

/**
* this code is made by C_19_25
* Problem: 495
* Result: Accepted
* Submission Date: 2019-04-14 11:34:13
* Time: 40ms
* Memory: 1016KB
*/
#include <bits/stdc++.h>
#define eps 1e-8
typedef long long ll;
using namespace std;
const int maxn = 1e6;
const int INF = 1e9;
const ll linf = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
//第一行有两个整数 n, q 分别表示一开始排队的人数n,以及之后的q次操作  1 <= n, q <= 100000.
//
//第二行有n​个数 a_1 .... a_n​,分别表示 第 i​ 个人所需要花费的时间 1 <= a_i <= 1000000000​.
//
//之后有q行,每行表示一个操作
//
//Ins x 表示在队尾排入一个花费时间为 x 的人
//Out x 表示从队首有 x 个人离开
//Query 表示查询当前时刻,正在排队的人所用的总时间是多少(不考虑队首那个人的奶茶已经做了多久)
//Back x 表示时间回到 x 次操作之前
//(时间回溯表示在回溯的x次操作当中,离开的人又回来了,
//而这 x 次操作中新来排队的人好似从来没有出现过,之后会不会排队,不知道,题目保证时间回溯合法)
//Output
//
//对于每次的 Query 步骤,输出一行,该行只有一个数字,表示当前时刻正在排队的人所需要的总时间。
//Sample Input
//
//3 5
//1 2 3
//Ins 4
//Out 2
//Query
//Back 2
//Query
//
//Sample Output
//
//7
//10

int n,q;
ll a[maxn];
ll ans[maxn];
ll sum[maxn];
int st[maxn],ed[maxn];
int main()
{
    scanf("%d %d",&n,&q);
    for(int i = 1; i<=n; i++)
    {
        scanf("%lld",&a[i]);
        sum[i] = sum[i-1]+a[i];
    }
    ans[0] = sum[n];
    st[0] = 1,ed[0] = n;
   // cout<<sum[n]<<endl;
    for(int i = 1; i<=q; i++)
    {
        char s[10];
        scanf("%s",s);
        if(s[0] == 'I')
        {
            int x;
            scanf("%d",&x);
            st[i] = st[i-1];
            ed[i] = ed[i-1]+1;
            sum[ed[i]] = sum[ed[i]-1]+x;
        }
        else if(s[0] == 'O')
        {
            int x;
            scanf("%d",&x);
            st[i] = st[i-1] + x;
            ed[i] = ed[i-1];
        }
        else if(s[0] == 'Q')
        {
            ed[i] = ed[i-1];
            st[i] = st[i-1];
            cout<<sum[ed[i]]-sum[st[i]-1]<<endl;
        }
        else
        {
            int x;
            scanf("%d",&x);
            st[i] = st[i-x-1];
            ed[i] = ed[i-x-1];
        }
         //cout<<sum[ed[i]]-sum[st[i]-1]<<endl;
    }



    return 0;
}

H

H - 钢铁雄心

Time Limit: 1000/1000MS (C++/Others) Memory Limit: 262144/262144KB (C++/Others)

Problem Description

钢铁和雄心一起玩了一款游戏叫做《钢铁雄心》。

SGH并不知道这款游戏是干什么的,但是因为被嘲笑了,所以决定用《钢铁雄心》来难为大家。

钢铁和雄心分别在纸上写下一个数字,然后由SGH摇出骰子, 钢铁和雄心谁写的数字离摇出的数字更接近,谁获胜,相同则平局。现在问谁获胜的可能性更大?

SGH使用的是六面骰骰子,数字1 ~ 6 且出现概率相同(SGH怎么可能会用假骰子呢?他是个好人啊 ~~(笑)~~

Input

第行输入一个数字 T <= 1e5 表示共有 T 组样例 。

之后 T 行,每行包含两个数字 a, b,分别表示钢铁和雄心写下的数字 。

Output

如果钢铁获胜的可能性更大,输出 “GangTie NB!!”

如果雄心获胜的可能性更大,输出 “XiongXin TQL!!”

如果两个人获胜的可能性相同,输出 “ShadowGhostH CaiCai!!!”

Sample Input

2
1 10
5 5

Sample Output

GangTie NB!!
ShadowGhostH CaiCai!!!

 

思路:判断一下数和1到6的距离就行了。

/**
* this code is made by C_19_25
* Problem: 496
* Result: Accepted
* Submission Date: 2019-04-14 09:24:55
* Time: 160ms
* Memory: 592KB
*/
#include <bits/stdc++.h>
#define eps 1e-8
using namespace std;
typedef long long ll;
const int maxn = 1e6;
const int INF = 1e9;
const ll linf = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
ll mod = 1e9+7;
ll x,y;


int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>x>>y;
        double d1 = 0,d2 = 0;
        for(int i = 1; i<=6; i++)
        {
            d1 += abs(x-i);
            d2 += abs(y-i);
        }
        //d1 /= 6.0;
        //d2 /= 6.0;
        if(d1 == d2)
            cout<<"ShadowGhostH CaiCai!!!"<<endl;
        else if(d1<d2)
            cout<<"GangTie NB!!"<<endl;
        else
            cout<<"XiongXin TQL!!"<<endl;
    }


    return 0;
}

I

I - 擦数字

Time Limit: 1000/1000MS (C++/Others) Memory Limit: 262144/262144KB (C++/Others)

Problem Description

SGH 这几天认识了一个新朋友叫武鹤司(SGH 天天都在认识一些什么人啊!)这天他们在一起玩游戏,删除一些数字的游戏。

规则是这样的,因为今年是 2019 年,所以他们希望对于任意一个数字,可以擦去若干个数位之后,得到 2019 例如:1230189 在擦去1, 3, 8 之后就的到了 2019 ,没错这四个数字的顺序是不应该被改变的。9102 就不是一个擦除后能够得到 2019 的数字。

现在,SGH 和武鹤司想知道不大于 M 的所有正整数中,有多少种擦除后能够得到 2019 的方式。

注意哦,因为是多少种,所以 22019 这一个数字就可以贡献两种方法。

Input

第一行输入一个数字 T < 10 表示共有 T 组样例 

之后T 行,每行包含一个数字 1e4 < M < 1e9 

Output

输出不大于 M 的所有数中,能够生成 2019 的方案数

Sample Input

2
2019
20000

Sample Output

1
2

思路:DFS+剪枝,预处理把所有带2019的数字都跑出来,先把2019填上,其他除了第1位不能填外随便填。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <map>
#include <vector>
#include <queue>
#include <set>
#define eps 1e-8
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 1e8;
const int INF = 1e9;
const ll linf = 0x3f3f3f3f3f3f3f3f;
using namespace std;
int n;
int cnt;
int number[maxn];
int num[16];
int a,b,c,d;
void dfs(int pos, int len)
{
    //cout<<"1111"<<endl;
    if(pos == len+1)
    {
        ll tmp = 0;
        for(int i = 1; i<=len; i++)
            tmp = 10*tmp + 1*num[i];
        number[cnt++] = tmp;
        return;
    }
    if(pos == a|| pos == b || pos == c || pos == d)
    {
        dfs(pos+1,len);
        return;
    }
    if(pos == 1)
    {
        for(int i = 1; i<=9; i++)
        {
            num[pos] = i;
            dfs(pos+1,len);
        }
    }
    else
    {
        for(int i = 0; i<=9; i++)
        {
            num[pos] = i;
            dfs(pos+1,len);
        }
    }
}

void init()
{
    cnt = 0;
    for(int len = 4; len<=9; len++)
    {
        for(a = 1; a<=len; a++)
            for(b = a+1; b<=len; b++)
                for(c = b+1; c<=len; c++)
                    for(d = c+1; d<=len; d++)
        {
            memset(num,0,sizeof num);
            num[a] = 2,num[b] = 0,num[c] = 1,num[d] = 9;
            dfs(1,len);
        }
    }
}
int main()
{
    init();
    //cout<<cnt<<endl;
    sort(number,number+cnt);
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n;
        int ans = upper_bound(number,number+cnt,n)-number;
        cout<<ans<<endl;

    }

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值