国庆清北刷题冲刺班 Day6 上午

1. 角谷猜想

(kakutani.pas/c/cpp)
(kakutani.in/out)
时间限制:1s/空间限制:256M
【题目描述】
某个名字末尾是 654321 的小 A 同学是个大家眼中公认的学霸(虽然他永远
不承认),他对题目的直觉到了一种可怕的地步,一眼看出题目的算法对他而言
只是小 Case,他甚至能在看到一个证明的瞬间敏锐地判断出这个证明的真伪。
现在小 A 同学机缘巧合地看到了角古猜想 (即对于 x 当它为奇数则 1 3   x x ,
为偶数,则
2
x
x  ,一直重复这个步骤,则 x 最终会变为 1),在看完这个猜想的
一瞬间,他的直觉就来了——他认为角古猜想一定是错的!然后——他立刻就能
找出反例!
他立刻在纸上写满了 ) 00 10 1 (   n n 个小于 ) 10 0 ( 10
4
  L
L
的正整数, 打算放
到他的 grand super computer 上去跑,可是他突然觉得有些正整数不是很吉利,
可能会干扰到他的最终结果,所以他打算把一些正整数加工一下。
小 A 觉得 4、7、13 都是不吉利的数字,所以要把所有正整数里的 4、7、13
都去掉,如果去掉后得到的新数字里依旧有 4、7、13,那么就要继续删掉,直
到最后的数组不存在 4、7、13,它才是一个吉利的数字。
例如 1 => 113 => 11133 => 111733 => 1411733
特别规定,如果最后所有数字都被删掉了,就输出 0
小A觉得这个枯燥的工作不适合他这样的天才, 于是就把这个工作交给了你。
当然, 只要你能顺利解决,小 A 承诺会在那篇将会震惊世界的论文的特别感谢栏
上署上你的大名。
【输入格式】 (kakutani.in)
一共 1  n 行。
第一行一个正整数 ) 0 10 1 (   n n ,表示数字个数。
接下来每行一个正整数 x 。
【输出格式】 (kakutani.out)
一共 n 行。
每行一个正整数,表示输入每个 x 对应的答案。
【输入输出样例】
kakutani.in kakutani.out
5
13713
141713
1333333372589
1411733
2147483647
0
11
3333332589
1
21836
【数据规模约定】
对于 10% 的数据, 2147483647 0   x
对于另外 10% 的数据,给定的数字 x 没有数码 3
对于另外 10% 的数据, 1  n
对于全部的数据, ) 00 10 1 (   n n ,
L
x 10 1   )

国庆清北刷题冲刺班 Day6 上午_第1张图片

//写个栈 记录一下就好 我当时竟然傻逼到写 双向链表
#include 
#include 
const int MAXN = 131072;
char str[MAXN];

int main() {
    int n, len, m, top;
    freopen("kakutani.in", "r", stdin);
    freopen("kakutani.out", "w", stdout);
    scanf("%d", &n);
    for (; n; n--) {
        scanf("%s", str);
        len = strlen(str), m = 0, top = 0;
        for (int i = 0; i < len; i++)
            if (str[i] == '1') top++;
            else if (str[i] == '4' || str[i] == '7') continue;
            else if (str[i] == '3') {
                if (top > 0) top--;
                else str[m++] = str[i];
            }
            else{
                for ( ; top > 0; top--) str[m++] = '1';
                str[m++] = str[i];
            }
        for ( ; top > 0; top--) str[m++] = '1';
        str[m] = '\0';
        if (m == 0) puts("0");
        else printf("%s\n", str);
    }
    fclose(stdin); fclose(stdout);
    return 0;
}

考场AC代码:

#include 
#include 
#include 
using namespace std;
#define rg register
#define Max 100100
char str[Max];
int nxt[Max], pre[Max];
int main (int argc, char *argv[]) {
    freopen("kakutani.in","r",stdin);
    freopen("kakutani.out","w",stdout);
    int N, Len, Count;
    scanf ("%d", &N);
    rg int i, j;
    for(; N ; -- N){
        scanf("%s",str + 1);
        Len=strlen(str + 1);
        nxt[0]=1,Count=Len;
        for(i = 1; i <= Len + 1; ++ i) nxt[i]=i + 1,pre[i]=i - 1;
        for(i = 1; i <= Len; i = nxt[i])
            for (; str[i] == '4' || str[i] == '7' || (str[pre[i]] == '1' && str[i] == '3') || (str[nxt[i]] == '3' && str[i] == '1'); ) {
                for(;str[i]=='4';)
                    str[i]=str[nxt[i]],nxt[i]=nxt[nxt[i]],pre[nxt[i]]=i,nxt[pre[i]]=i,--Count;
                for(;str[i]=='7';)
                    str[i]=str[nxt[i]],nxt[i]=nxt[nxt[i]],pre[nxt[i]]=i,nxt[pre[i]]=i,--Count;
                for(;str[nxt[i]]=='3'&&str[i]=='1';)
                    Count-=2,str[i]=str[pre[i]],pre[i]=pre[pre[i]],nxt[pre[i]]=i,nxt[i]=nxt[nxt[i]],pre[nxt[i]]=i;
                for(;str[pre[i]]=='1'&&str[i]=='3';)
                    Count-=2,str[i]=str[nxt[i]],nxt[i]=nxt[nxt[i]],pre[nxt[i]]=i,pre[i]=pre[pre[i]],nxt[pre[i]]=i;
            }
        if(!Count){ puts("0"); continue; }
        else for(i=0;i<=Len;i=nxt[i]) if(str[i]!='\0') putchar(str[i]);
        putchar ('\n');
    }
    fclose(stdin);fclose(stdout);
    return 0;
}

2. 刀塔

(dota.pas/c/cpp)
(dota.in/out)
时间限制:2s/空间限制:256M
【题目描述】
事情要从小 A 的朋友小 S 说起,小 S 是个刀塔狂热粉,他每天除了学习就是
在打刀塔。然而让小 S 很苦恼的事情是,他发现最近他似乎遇见瓶颈了,他发现
他每次输的时候都是雪崩, 赢的时候都是躺赢, 完全发挥不出自己应该有的实力。
他上贴吧请教了三分钟辉耀羊刀的万分大神,接到了万分大神的圣旨:去看自己
的录像反省反省。于是小 S 决定利用十一假期的时间好好反省反省。
小 S 调出了自己前段时间的游戏录像,一共有 N 个录像,他给每个录像一个
正整数表示这个录像的观看价值。 现在他决定从里面找到二组连续的录像来观看,
这两组录像的要求如下:
1.两组录像里每一组的录像数量都不能小于 A,不然没有意义
2.他看的总录象的数量一定要超过 B,他相信看的越多就越好
3.小 S 觉得两组录像时间隔的太近没有意义,因为很有可能前后两段暴露的
问题基本一致,但是他又觉得隔的太远也干扰他去思考自己当时的状态。所以他
要求这两段录像中间应该刚好隔了 K 个录像
好吧,这已经够让人烦躁的了,但是小 S 还不满足,他觉得这样的挑选方案
依旧很多,所以他想挑选足够好的方案,一个质量足够高的方案——也就是观看
价值最低的录像的观看价值要尽可能的高。
【输入格式】 (dota.in)
一共 2 行。
第一行四个正整数 K B A N 、 、 、 。
接下来一行 N 个正整数,表示每个录像的观看价值 ) 10 1 (
9
  x x 。
【输出格式】 (dota.out)
一共 1 行,表示最佳方案中观看价值最低的录像的观看价值。
【输入输出样例 1】
dota.in dota.out
10 2 5 3
7 8 2 3 1 6 4 10 5 9
4
【输入输出样例 2】
dota.in dota.out
20 3 9 3
54867025 259306632 473619223 170507035
347936959 421059860 246006182 948910354
630205869 541359081 574152766 665959900
843439075 445125437 774018043 719562887
705993886 133173428 256457367 708196876
246006182
【数据规模约定】
国庆清北刷题冲刺班 Day6 上午_第2张图片

//二分答案
#include 
#include 
using namespace std;
const int MAXN = 2097152;

int N, A, B, K;
int x[MAXN];
int l[MAXN], r[MAXN];
bool check(int c) {
    for (int i = 1; i <= N; i++)
        if (x[i] >= c) l[i] = l[i - 1] + 1;
        else l[i] = 0;
    for (int i = N; i >= 1; i--)
        if (x[i] >= c) r[i] = r[i + 1] + 1;
        else r[i] = 0;
    for (int i = 1; i <= N; i++)
        if (l[i - 1] >= A && r[i + K] >= A && l[i - 1] + r[i + K] >= B)
            return true;
    return false;
}

inline void read(int &x){
    x=0; int f=1; register char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;
}

int main() {
    freopen("dota.in", "r", stdin);
    freopen("dota.out", "w", stdout);
    int left = 99999999, Mid, right = -19999999,Ans;
    read(N),read(A),read(B),read(K);
    for (int i = 1; i <= N; i++)
        read(x[i]), left = min(left, x[i]), right = max(right,x[i]);
    --left, ++right;
    while (left <= right) {
        Mid = (left + right) >> 1;
        if (check(Mid)) left = Mid + 1 ,Ans = Mid;
        else right = Mid - 1;
    }
    printf("%d\n", Ans);
    return 0;
}

3. 反击数

(spenum.pas/c/cpp)
(spenum.in/out)
时间限制:2s/空间限制:256MB
【题目描述】
上次说到小 A 在你的帮助下,在反证角谷猜想的路上已经看见了曙光,他相
信自己即将为这个著名难题画上最后的句点,小 A 十分地兴奋,结果他那微不足
道的老毛病又犯了——他忍不住想炫耀一番,结果搞得朋友圈内人尽皆知,当然
也就传到了另一位学霸小 H 的耳中。
虽然表面上小 H 和小 A 是挚友,但其实不难想象,这两个天才在私下里早已
将对方视为此生必须要战胜的对手。和天赋异禀的小 A 不同,小 H 拥有的是逆天
的气运——经常他选择题都不需要看选项就能全对 (据说这也是曾经笃信唯物主
义的小 A 为什么变得神经质般迷信的根本原因) 现在小 H 要正式向小 A 在角谷猜
想上发起反击, 不过两个学霸在某一点上倒是达成了一致——这个猜想一定是错
的,所以它必然有反例!
对自己的气运抱有足够自信的小 H 打算用这样的方式来找到反例:他先选定
一个自己的幸运数 X,他认为所有中间出现了 X 的数都是“扩展幸运数” (包括
X 自身) (例:若 X=69 那么 84576901 就是一个“扩展幸运数” ,在 84576 后面
出现了数码 69,而 679 则不是一个“扩展幸运数” ),然后小 H 会再精心挑选一
个“命运数”K,最后小 H 将在随机生成的正整数区间[L, R]中选择第 K 大的“扩
展幸运数” ,用它去验证角谷猜想。小 H 认为这样的速度一定快过小 A 那个落后
的办法。
好了,现在同样的工作摆在了你的面前,你需要帮助小 H 得到那个数字。
PS:小 H 有时候会突然无征兆地打瞌睡(为了保养他的运气) ,所以可能正
整数区间[L, R]中并没有 K 个“扩展幸运数” ,这时候输出“Hey,wake up!” (不
含引号)
【输入格式】 (spenum.in)
输入仅有一行,共 4 个数字,按次序分别为 L,R,X,K
【输出格式】 (spenum.out)
输出为一行,即正整数区间[L, R]中第 K 大的“扩展幸运数”
【输入输出样例 1】
spenum.in spenum.out
1 1000 6 14 67
【输入输出样例 2】
spenum.in spenum.out
1 1 2 1 Hey,wake up!
【样例解释】
前 14 个数字分别是 6、16、26、36、46、56、60、61、62、63、64、65、66、67
【数据规模约定】
对于 10% 的数据 : 1000000 1    L R
对于另外 10% 的数据 : 1  K
国庆清北刷题冲刺班 Day6 上午_第3张图片

T3 抄的题解。。

#include
#include
#include
#include

using namespace std;
typedef unsigned long long ULL;
char p[32];
int m,nxt[32];

void KMP(){
    nxt[0] = 0,nxt[1] = 0;
    for(int i=1; iint j = nxt[i];
        while(j && p[i]!=p[j]) j = nxt[j];
        nxt[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
int a[32];
ULL dp[20][2000][2];

ULL DFS(int k,int w,bool lim, bool get) {
    if(!k) return get;
    else if(!lim && ~dp[k][w][get]) return dp[k][w][get];
    else {
        ULL ret = 0;
        for (int i = 0, j = lim ? a[k] : 9; i <= j; i++) {
            int t = w;
            while (t && p[t] - '0' != i) t = nxt[t];
            if (p[t] - '0' == i) t++;
            ret += DFS(k - 1, t, lim && (i == a[k]), get || (t == m));
        }
        return lim ? ret : dp[k][w][get] = ret;
    }
}

ULL query(ULL x){
    int top = 0;
    while(x) {
        a[++top] = x % 10;
        x /= 10;
    }
    memset(dp,0xff,sizeof dp );
    return DFS(top,0,true,false);
}

int main(int argc,char *argv[]){
    freopen("spenum.in", "r", stdin);
    freopen("spenum.out", "w", stdout);
    ULL L,R,K;
    scanf("%I64u %I64u %s %I64u",&L,&R,p,&K);
    m = strlen(p);
    KMP();
    K += query(L - 1);
    if (query(R - 1) < K ) {
        puts("Hey,wake up!");
        return 0;
    }
    ULL tmp = 1 ,Ans = L - 1;
    while(tmp < R - L + 1) tmp <<= 1;
    while(tmp){
        if(query(Ans + tmp) < K) Ans += tmp;
        tmp >>= 1;
    }
    printf("%I64u",Ans + 1);
    return 0;
}

你可能感兴趣的:(搜索,动态规划,二分答案,模拟,模拟考试,二分答案,模拟,KMP,搜索)