【10.16 胡测】Day4 第四波胡策题

来源:互联网 发布:mac cad软件 编辑:程序博客网 时间:2024/05/16 05:02


尬·在底下评测一路绿上头评测一抹灰

WA一声哭出来,自己的码力真的太渣,读入都读不进来明明都红字标识出来了看题还是看错w还脸黑本地A机测CE【哭晕】

摆在最前面的出题人

(排名以奇怪の博主序)
lxt 小彤√
zzy zzy
lwq 吕婉晴
wyx 痴汉wyx【跑】

  • 摆在最前面的出题人
  • Problem 1 埃罗芒阿老师
    • 题目描述
    • 思路
  • Problem 2 名侦探柯南
    • 题目描述
    • 思路
  • Problem 3 中二病
    • 题目描述
    • 吐槽
    • 思路
  • Problem 4 银魂
    • 题目描述
    • 思路

Problem 1 埃罗芒阿老师

题目来源

http://codevs.cn/problem/2913/

题目描述

埃罗芒阿老师是著名的插画家,她的工作是为电击文库出版的的书画插画。
快要到截稿日了,埃罗芒阿老师还在水>_<
埃罗芒阿突然发现自己还有一大堆插画没有完成,如果不能在截稿时间内完成是要扣工资的。
于是埃罗芒阿老师把每个任务所需的时间和现在距离每个任务截稿的时间记录了下来,想要计算出最多可以完成多少任务。

输入描述


第一行是一个整数N,
接下来N行每行两个整数T1,T2描述一个任务:完成这个任务需要T1秒,如果在T2秒之内还没有完成任务,这个任务就到截稿时间了。

输出描述

输出一个整数S,表示最多可以完成S个任务.

样例输入


4
100 200
200 1300
1000 1250
2000 3200

样例输出


3

数据范围及提示


对于30%的数据,N≤100;
对于60%的数据,N≤10000;
对于100%的数据,N < 150,000; T1 < T2 < INT_MAX;
所有数据保证随机生成。

思路:


2913 建筑抢修
一上来完美地看错题面,以为是个裸的线段覆盖系列
感人的样例让我轻松偷税的水过去然后评测WA的十声哭出来
分析得出
应该按照按截稿日期由小到大排序
从第一个开始,若当前任务所需时间加之前的总时间小于等于当前截稿时间,扔进堆里,统计
如果是大于,这时要从它前面所有任务中选一个修理时间最长的和它比较
若当前任务所需时间更长,则不扔进堆中;若当前任务所需时间较短,则替换成当前任务
注意如果截稿日期相同的情况下优先选择所需时间最小的
还有这个题竟然上来就是long long在看对了题目后又翻一波车

代码如下:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<queue>#define ri register long longtypedef long long LL;using namespace std;const int sz = 200010;inline void read(LL &x){    x=0;bool f=0;char c=getchar();    while(c<'0'||c>'9')    {if(c=='-') f=1;c=getchar();}    while(c>='0'&&c<='9')    {x=x*10+c-'0';c=getchar();}    if(f) x*=-1;}inline void write(LL x){    if(x<0)        putchar('-'),x*=-1;    if(x/10)        write(x/10);    putchar(x%10+'0');}struct ed{    LL done,need;}l[sz];priority_queue<ed>q;inline bool cmp(ed x,ed y){    if(x.need==y.need)        return x.done<y.done;    else        return x.need<y.need;}bool operator < (ed a,ed b){    return a.done < b.done;}LL n,ans,tot;int main(){    read(n);    for(ri i=1;i<=n;++i)        read(l[i].done),read(l[i].need);    sort(l+1,l+n+1,cmp);//必不可少     for(ri i=1;i<=n;++i)    {        if(l[i].done+tot<=l[i].need)        {            ans++;            q.push(l[i]);//是把这个点压进去emmm             tot+=l[i].done;        }        else        {            ed now=q.top();//ed下保证下一步的比较             q.pop();            tot-=now.done;//先扔下这个点的时间,不是扔下已有点的......             if(l[i].done<now.done)            {                q.push(l[i]);                tot+=l[i].done;            }            else            {                q.push(now);                tot+=now.done;            }        }    }    write(ans);    return 0;}

Problem 2 名侦探柯南

题目来源

http://codevs.cn/problem/1089/

题目描述

铃木次吉郎又一次向基德发出挑战,,而这次的挑战是在铃木号快车上,这辆一年只运行一次的快车上,乘客几乎是固定不变的,尤其是7号车厢,这节车厢里的乘客每年都会提前预定,而这么做的理由也只是为了参加在这辆车上独有的推理谜题,不幸的是,因为今年毛利小五郎的出现,在这节车厢中出现真的杀人事件,现在为了找出凶手,车厢中的人被聚集到一起,共有N个人,他们一共会说P句证词,但N个人中会有M个人说谎,但凶手只有一个,因为柯南还在寻找其他证据,为此你要通过他们说的话去判断凶手是谁。

输入描述
输入第一行为3个数,N,M,P,分别表示有N个人,M个人说谎,P句证词
以下N行,每行一个人名(全部大写)
之后P行,每行开始为一个人名,后紧跟一个冒号和一个空格,后面是一句证词(符合表中所列的格式,可能有废话)证词每行不超过250个字符
输入中不会出现连续的两个空格,且每行开头和结尾也没有空格。

单词注明:guilty
角色注明:
铃木次吉郎:铃木财团顾问,爱好环游世界,在得知有关基德的事件后,扬言要亲手逮捕基德(用自家的宝石来作为诱饵)
基德(KID):本名黑羽快斗,为调查父亲死亡真相而成为怪盗寻找珍稀的宝石,以此找出幕后真相,也以铃木拿宝石挑战作契机寻找宝石(详情请见怪盗基德1412)
江户川柯南:原名工藤新一(滚筒洗衣机),在服用酒厂药物后变小而以柯南为名掩盖自己未死的真相
毛利小五郎:一直划水的侦探

输出描述
输出只有一行,有三种情况:
1 , 若确定一个凶手,则输出凶手名字
2 , 找到符合条件的凶手,但有多个,输出” Cannot Determine ”(不含引号)
3 , 没有找到符合条件的凶手,即根据已知条件不能确定任何一个可能的凶手,输出“Impossible”(不含引号)。

样例输入
3 1 5
MIKE
CHARLES
KATE
MIKE: I am guilty.
MIKE: Today is Sunday.
CHARLES: MIKE is guilty.
KATE: I am guilty.
KATE: How are you??

样例输出
MIKE

数据范围及提示
1<=N<=20 , 0<=M<=N ,1<=P<=100
数据保证不随机生成。
凶手保证在N个人中,但他/她不一定说过话。

思路:


读入不会。。。。。。。
1089 侦探推理
第一层枚举犯人 20
第二层枚举星期几 7
n*p*7
对于证词中的两个关键元素判断
看看星期几看符不符合,
犯人看是否匹配,同时和我们枚举的犯人比较
因为枚举的是犯人,所以m时,标记
char 读进来,遇到空格停下,再string处理
啧,感觉麻烦得很直接上海jump【雾】

代码如下:不想做粘贴题解了。。。。。

//num记录编号为i的人的名字,name记录名字的编号 #include<iostream>#include<cstdio>#include<map>#include<cstring>#include<string>#include<algorithm>using namespace std;int n , m , p , t , fl , ans = 0;int tf[25];string s , now , num[25];map<string , int>name;struct st{    int id;    string ta;}f[105];const string days[]={    "Today is Monday.",    "Today is Tuesday.",    "Today is Wednesday.",    "Today is Thursday.",    "Today is Friday.",    "Today is Saturday.",    "Today is Sunday.",};bool check(int a, bool b){    if(tf[a] == -1)    {        if(!b)         fl ++ ;//说谎的人         else t ++ ;//诚实的人         if(fl > m)//说谎的人大于m             return 1;        if(n - t < m)//诚实的人大于n-m             return 1;    }    if(tf[a] == -1)    {        tf[a] = b;return 0;//标记这个人的状态 0说谎 1诚实     }    else    {        if(tf[a] == b)//没有矛盾             return 0;        return 1;//出现矛盾  一个人既说谎又诚实     }}void run(int cr , string day){    memset(tf , -1 , sizeof(tf));    t = fl = 0;    for(int i = 1 ; i <= p ; i ++)    {        int pos;        pos = f[i].ta.find("I am guilty.");        //s.find(str)在s中寻找str这个字符串,返回起始位置的迭代器         //如果不存在 返回-1  -1取反为0         if(~pos)        {            if(check(f[i].id , f[i].id == cr)) return ;        }        pos = f[i].ta.find("I am not guilty.");        if(~pos)        {            if(check(f[i].id , f[i].id != cr))return ;        }        pos = f[i].ta.find(" is guilty.");        if(~pos)        {            now = f[i].ta;            now.erase(pos , 11);            int bm = name[now];            if(check(f[i].id , bm == cr))return ;        }        pos = f[i].ta.find(" is not guilty.");        if(~pos)        {            now = f[i].ta;            now.erase(pos , 15);            int bm = name[now];            if(check(f[i].id , bm != cr))return ;        }        pos = f[i].ta.find("Today is ");        if(~pos)        {            if(check(f[i].id , f[i].ta == day))return ;        }    }    if(ans&& ans != cr)    {        cout<<"Cannot Determine";        exit(0);        //exit()通常是用在子程序中用来终结程序用的,使用后程序自动结束,跳回操作系统。        //exit(0) 表示程序正常退出,exit⑴/exit(-1)表示程序异常退出。     }    ans = cr;}int main(){    freopen("kid.in","r",stdin);    freopen("kid.out","w",stdout);    cin>>n>>m>>p;    for(int i = 1 ; i <= n ; i ++)    {        cin>>num[i];        name[num[i]] = i ;    }    for(int i = 1 ; i <= p ; i ++)    {        cin>>s;        s.erase(s.length()-1,1);        //s.erase(pos,len)删除起始于 pos位置 长度为len的子串         f[i].id = name[s];        getline(cin , f[i].ta);        //读取整行文本到f[i].ta         f[i].ta.erase(0,1);    }    for(int i = 1 ; i <= n ; i ++)    for(int j = 0 ; j <= 6 ; j ++)        run(i,days[j]);    if(ans == 0)    cout<<"Impossible";    else cout<<num[ans];    return 0;}

Problem 3 中二病

题目描述

一天,翻阅Dark Flame Master黑暗笔记的邪王真眼使发现了笔记中所记载的不可視境界線的秘密。
在黑暗笔记的某一页,她看见了一篇文章。这篇文章里记载着寻找不可視境界線并前往异世界的方法。
这篇文章由英文字母、和空白字符(制表/空格/回车)构成,但由于管理局的介入,字母的大小写变得十分混乱,换行和空格也不成章法。
Dark Flame Master告诉邪王真眼使,这篇文章中其实隐含着一个数字x,只要朝向不可視境界線并说出x次“闇の炎に抱かれて消えろっ!”就可以打开异世界的通道,这个x就是”Hello World”在文章中作为子序列出现的次数。
于是邪王真眼使重新阅读了笔记并解放了“じゃおう シンガン”的力量!
由于解放后的“じゃおう シンガン”力量十分强大,所以大小写对于她而言毫无区别;因此,“hEllOWorLD”这样的子序列是可以的。所有的空格回车和制表符都可以被她直接忽略掉;也就是说,“HelloWorld”是可以的;当然,“hE Llow oR ld”也是可以的。
现在,借助邪王真眼使的力量,Dark Flame Master需要帮助她计算出在这篇文章中“Hello World”作为子序列出现的次数。
由于答案可能很大,请输出结果对1000000007(10^9+7)的余数。

输入描述
输入包含若干行。这些行的内容共同构成一篇文章(由于管理局的介入,十分可能出现语法不通顺的情况)。
文章以EOF(文件结尾)结束。

输出描述
输出仅包含一个整数,表示这篇文章中“Hello World”出现的次数。

样例输入1
HhEeLlLlOoWwOoRrLlDd

样例输出1
1536

样例输入2
Gou Li Guo Jia Sheng Si Yi
Qi Yin Huo Fu Bi Qu Zhi
River can feed people
Also can race boats
Hall Ellen Ok Words locked

样例输出2
273

数据范围及提示
记n为输入的文章的长度(字符数)。
对于20%的数据,n <= 20。
对于50%的数据,n <= 500。
对于所有的数据,15 <= n <= 500000。
数据不保证随机生成。(╯▽╰)
被漆黑烈焰吞噬殆尽吧!

吐槽:


没看错,这次不说思路而是先吐槽一通
真的体现了zzy高超的中二技巧,好歹我最多出了个达拉崩吧版的题面emmmm
这个体面我竟然没在看题而是在认真的读题面↑_↓

思路:


读入被卡爆的尴尬,用的按行读入想仅仅靠修改按行改成全篇来着,
结果走上死胡同晕掉,最后华丽丽的gg
简直有清北卡精度时候一样蓝瘦。。。。。。

小彤讲的题233333【做笔记】
“如果i位字符编号可以是j
f[i-1][j] 它之前的长度为j的方案数可以累加到它
f[i-1][j-1] 它之前长度为j-1的方案数,末尾元素换成了i,是全新的方案,所以也可以累加到它
滚动数组”
↑原话


被强烈要求删掉的版本:

#include <iostream>#include <cstdio>#include <cstdlib>using namespace std;const int mod=1000000007;int main(){    long long dp[11]={1};    char c,hello[20]="?helloworld";    while((c=getchar())!=EOF)    {        for(int i=10;i>=1;i--)            if(c==hello[i]||c+32==hello[i])            dp[i]=(dp[i-1]+dp[i])%mod;    }    cout<<dp[10];    return 0;}


个人认为写的还不如上面好看的版本emmm:

#include<iostream>#include<cstdio>#include<algorithm>using namespace std;const int N=500000+500,mod=1000000007;char a;int tot;long long f[50];bool d[N][15];void read(){    while(scanf("%c",&a)!=EOF){        if(a>='A'&&a<='Z') a=a+32;        switch(a){            case('h'):{                tot++;                d[tot][1]=1;                break;            }            case('e'):{                tot++;                d[tot][2]=1;                break;            }            case('l'):{                tot++;                d[tot][3]=1;                d[tot][4]=1;                d[tot][9]=1;                break;            }            case('o'):{                tot++;                d[tot][5]=1;                d[tot][7]=1;                break;            }            case('w'):{                tot++;                d[tot][6]=1;                break;            }            case('r'):{                tot++;                d[tot][8]=1;                break;            }            case('d'):{                tot++;                d[tot][10]=1;                break;            }        }    }}int main(){    read();     f[0]=1;    for(int i=1;i<=tot;i++){        for(int j=10;j>=1;j--){            if(d[i][j]){                f[j]=(f[j]%mod+f[j-1]%mod)%mod;            }        }    }    printf("%lld",f[10]);    return 0;}

Problem 4 银魂

题目描述


银桑、神乐、新八三人在测试阿姆斯特朗回旋加速喷气式阿姆斯特朗炮的威力。
阿姆斯特朗回旋加速喷气式阿姆斯特朗炮十分神奇,使用方式如下:
输入两个数字n,k到阿姆斯特朗回旋加速喷气式阿姆斯特朗炮的控制台中,然后阿姆斯特朗回旋加速喷气式阿姆斯特朗炮会计算出n!并把它转化为k进制。
最后n!在k进制下末尾0的个数就是本次发射的威力,每个0代表1点威力。
为了测试时不造成太大的破坏,三人想知道每次测试,发射的威力有多大。
现在给出多组测试的n和k,请计算出每次发射的威力。

输入描述
输入文件为amstl.in
题目包含多组数据,以EOF(文件结尾)为结束。
对于每组数据,输入一行两个正整数n,k;

输出描述
输出文件为amstl.out
每组数据一行,包含一个整数,表示本次发射的威力。

样例输入
10 40

样例输出
2
数据范围及提示
对于20%的数据,n <= 1000000, k = 10
对于另外20%的数据,n <= 20, k <= 36
对于100%的数据,n <= 10^12,k <= 10^12

思路:


部分引自大佬(话说大括号不换行真是异端【跑】)

朴素求法int j=n!;while(1){    if(j%k==0 )    {        cnt++,j/=k;        continue;    }    break;}return  cnt;

统计质因数的方法:
f(a)=n/a+n/(a^2)+n/(a^3)+…..+n/(a^k) a^k<=n,a^(k+1)>n
以从1-9中统计2为例(有2的只有2,4,6,8,);
2 4 6 8
2 √ √ √ √ 4
2^2 √ √ 2
2^31
显然,分层统计了2的次数;


然后是超级长的总结ww:
姿势不对100分思想手笨成了60分尴尬做法,遇到更尴尬的本地60教师机蜜汁CE……懵逼(:з」∠)这里写图片描述
在底下自测60水过结果无法编译(智熄)

纯暴力是靠long long阶乘,来模除求末尾0的个数,感人
比较费神费力的暴力是高精除+高精模,(难以想象这样做的孩子的痛苦,最后只抱着30哭)

像我这样的蒟蒻只会暴力搞事,将前面的阶乘分解之后(是log级别)
用错误的姿势去分解k,用的找素数模除
然后再去找阶乘因数暴力匹配除掉(大概不出错的话也是TLE 23333)
结果中间各种姿势不对又爆栈mmp导致后八个点一片粉

正解:

首先说一下在解释中出现的较重要的名词
模除:是我们统计末尾零的方法,先进行模10,看看是否有零,有的话进行除10,没有的话就结束,找完了结尾所有的零
匹配:因为是k进制,进制的规律满足如果是达到这个进制数那么进一位,
因此在阶乘中找到k那么答案就要更新一次
其实这么说也是不全面的,因为k为一个合数的时候不能直接找,
因此我们要对阶乘和k都进行质因数分解,最终在阶乘的因子中找k的因子
方法是如果这些因子%k的一个因子为0,那么将这个因子能产生几个k的这个因子记录下来

六十分中我们巧妙地对阶乘进行质因数分解,将阶乘整体化为阶乘元素来处理,大大加快了我们的实现速度
在六十分基础上进一步分解,将k进行因数分解,枚举范围为2~√n
Q:为什么是√n?
因为另一半可以依据√n中枚举的这一半来推出来
k是已知的枚举i为因子,看看i的情况,那么另一半也在√n内的
而且还不用判素数,
eg:对于4,在枚举2时,已经去除了因子中所有的2,不可能再分解出4来,优化不是一点两点
注意判断最后k剩下的是一个大于根k的质数的情况

这里比较巧妙的是在对k进行一次枚举模除之后
接着在阶乘的所有因子中进行匹配,在n!中k的质因数组合的个数即为末尾0的个数
最终在这些答案里取min即为满足的情况,
这样对于时间最终是(√k+logn)级别左右?
(毕竟一个合数的话要多个因子合成嘛,肯定要取个数最小的那个才满足能够合成这个k )
当然考场上自己能够实现才是关键……

代码如下:

#include<bits/stdc++.h>#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<queue>#include<cmath>using namespace std;typedef long long LL;#define ri register long longconst LL inf = 0x7ffffffffffll;inline void read(LL &x){    x=0;bool f=0;char c=getchar();    while(c<'0'||c>'9')    {if(c=='-')f=1;c=getchar();}    while(c>='0'&&c<='9')    {x=x*10+c-'0';c=getchar();}    if(f) x*=-1;}inline void write(LL x){    if(x<0)        x*=-1,putchar('-');    if(x/10)        write(x/10);    putchar(x%10+'0');}LL n,m,k,tot;LL ans=inf,n1=1;int main(){    freopen("amstl.in","r",stdin);    freopen("amstl.out","w",stdout);while(scanf("%lld%lld",&n,&k)>=2){    ans=inf;    for(ri i=2;i*i<=k;++i)    {        LL cnt=0;        for(;k%i==0;k/=i)            cnt++;        if(cnt==0)            continue;        LL res=0;        for(ri j=i;j<=n;j*=i)            res+=n/j;        ans=min(ans,res/cnt);    }    if(k!=1)    {        LL res=0,p=k;        while(k<=n)        {            res+=n/k;            k*=p;        }        ans=min(ans,res);    }    write(ans),puts("");}    return 0;}