2017.10.18队内互测——一场简单的模拟赛

来源:互联网 发布:淘宝点击图片跳转链接 编辑:程序博客网 时间:2024/06/03 19:40

出题人:dfkdsmbd,QQ,pingxing,maple

Problem 1 :

题目来源:原创改编自-http://codevs.cn/problem/1742/

题目描述

众所周知, wyh 是一名高二党,正把自己投入到学奥赛这一热火朝天的工作中。
在一个天高云淡、风和日丽的下午,你和神犇 wyh 又缓缓向着机房走去。 正当你准备上楼梯时, wyh 突发奇想, 提出了一个问题: 如果他一次能迈一阶台阶乃至多阶,那么他走到四楼一共有多少种可能的方案。 wyh 比较懒, 因此他还想知道他最少需要抬多少次腿(无论一次走几阶楼梯都算抬一次腿)。 因为某校比较穷,完工仓促, 所以修建的每个台阶之间的间距并不完全相同。其中有些台阶比较高, 有时一个台阶的高度甚至能比上多个台阶。现在已经规定好了一个台阶的标准高度(规定标准台阶的高度 1),我们就可以知道一到四楼所有台阶的相对高度。 因为腿长, wyh 一次最多能迈四个标准台阶的高度。因为 wyh 沉迷于和某外校大佬续火花以及毒瘤出题等活动无法自拔,所以他把这个问题扔给了平时最爱喊 666 和人生赢家 wyh的你。
输入描述
第一行两个整数 n,m。 n 表示 1 到 4 楼一共有多少台阶, m 表示会告诉你其中多少阶台阶的相对高度(其他未告诉的默认为 1)。接下来 m 行, 每行有两个整数 a,b。表示第 a 阶台阶的相对高度是b。(b 可能为 1)
输出描述
输出为一行,两个整数,分别为有多少种可能的方案和最少走多少步。方案数对 19260817 取模。
样例输入
3 1 2 4
样例输出
1 3
数据范围及提示
30% 1<=m<=n<=10
50% 1<=m<=n<=20
70% 1<=m<=n<=100
100% 1<=m<=n<=100000
思路
简单的DP(递推..)
记录累计高度对应的台阶编号,据此判断当前方案数可以由之前的哪些状态转移而来。
或者强行把每个不规则台阶强行拆成一个个的标准台阶,记录一下每个台阶拆分后的顶端,然后每次枚举到新的台阶,用其顶端的方案数来进行转移。或者搜索。
考场数组开太小,因为是Windows下评测…数组越界后pos又找回前面导致死循了…

代码

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#define RI register intusing namespace std;const int mod = 19260817;int n,m,a,b,sum;int add[100010];//累计高度 int pos[400010];//累计高度确定编号--注意要开4倍,因为将累计高度作为下标int hi[100010];//相对高度long long ans; long long f[100010];inline int read(){    char ch=getchar();    int ret=0;    while(ch<'0'||ch>'9')    ch=getchar();    while(ch>='0'&&ch<='9')    {        ret=ret*10+(ch-'0');        ch=getchar();    }    return ret;}int main(){    n=read();    m=read();    for(RI i=1;i<=n;i++)    hi[i]=1;    for(RI i=1;i<=m;i++)    {        a=read();        b=read();        hi[a]=b;    }    for(RI i=1;i<=n;i++)    {        sum+=hi[i];        add[i]=sum;        pos[sum]=i;    }    f[0]=1;    f[1]=1;    for(RI i=2;i<=n;i++)    {        for(RI j=4;j>=1;j--)        {            if((add[i]-j)>=0)            if(pos[add[i]-j]||add[i]-j==0)            f[i]=((f[i]%mod)+(f[pos[add[i]-j]])%mod)%mod;        }    }    for(RI i=0;i<n;i++)//贪心求最少步数     {        if(hi[i+1]<4)        {            for(RI j=4;j>=1;j--)            {                if(pos[add[i]+j])                {                    i=pos[add[i]+j]-1;                    break;                }            }        }        ans++;    }    printf("%lld %lld",f[n],ans);    return 0;}

Problem 2 :

题目来源:https://www.luogu.org/problem/show?pid=2656

题目描述

众所周知, a 是一名高三党,正把自己投入到学文化课这一热火朝天的工作中。国庆节长假时, 宅在家的 a 被妈妈拉去山上玩来缓解压力。 刚下了雨,山林里长满了蘑菇。这时妈妈提出了一个问题:a 要去 LWYZHS 森林里采蘑菇。LWYZHS 森林间有 N 个小树丛, M 条小径,每条小径都是单向的,连接两个小树丛,上面都有一定数量的蘑菇。a 经过某条小径一次,可以采走这条路上所有的蘑菇。由于 LWYZHS 森林是一片神奇的沃土,所以一条路上的蘑菇被采过后,又会长出一些新的蘑菇,数量为原来蘑菇的数量乘上这条路的“恢复系数” ,再下取整。
比如,一条路上有 4 个蘑菇,这条路的“恢复系数” 为 0.7,则第一~四次经过这条路径所能采到的蘑菇数量分别为 4,2,1,0.现在, a 从 S 号小树丛出发,求他最多能采到多少蘑菇。a 把这个问题交给了你。
输入描述
第一行, N 和 M。第 2……M+1 行,每行 4 个数字,分别表示一条小路的起点,终点,初始蘑菇数,恢复系数。第 M+2 行,一个数字 S。
输出描述 一个数字,表示 a 最多能采到多少蘑菇,在 int32 范围内。
样例输入 3 3
1 2 4 0.5
1 3 7 0.1
2 3 4 0.6
1
样例输出
8
数据范围及提示
对于 100%的数据, N<=80,000, M<=200,000, 0.1<=恢复系数<=0.8 且仅有一位小数, 1<=S<=N.
保证数据全部随机生成。
思路
对于在一个强连通分量内部的边,其蘑菇可以采到数量变为0,而对于强连通分量之间的边则只可以走一次。Tarjin求出S可以到达的强连通分量,处理出其内部能采到的全部数量赋为此强连通分量的权值,缩点将强连通分量的权值赋为点权,保留其他的边权求最长路(最短路算法,拓扑排序)即可。或者将强连通分量的权值变为缩点后点的自环权值,跑Dijkstra。
考场自信不缩点DFS了….全部TLE

代码

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<queue>#define RI register intusing namespace std;int n,m,ru,rv,tot,num,sum,cnt,s,maxn;int first[500010],nxt[500010],dfn[400010],low[400010],sccno[400010],siz[400010],stak[400010],staks[400010];int val[400010],Ed[400010],As[400010],dis[400010];double rw,a;double wws[400010],bs[400010];bool inq[400010];struct edge{    int u,v,w;    double b;}l[500010];queue<int>q;void build(int f,int t,double c,double a){    l[++tot]=(edge){f,t,c,a};    nxt[tot]=first[f];    first[f]=tot;}void dfs(int k){    dfn[k]=low[k]=++tot;    stak[++num]=k;    for(int i=first[k];i!=-1;i=nxt[i])    {        int x=l[i].v;        if(!dfn[x])        {            dfs(x);            low[k]=min(low[k],low[x]);        }        else if(!sccno[x])        low[k]=min(low[k],dfn[x]);    }    if(dfn[k]==low[k])    {        cnt++;        while(1)        {            sccno[stak[num--]]=cnt;            siz[cnt]++;            if(stak[num+1]==k)            break;        }    }}void SPFA(){    for(int i=1;i<=cnt;i++)    dis[i]=-7;    dis[sccno[s]]=val[sccno[s]];    q.push(sccno[s]);    inq[sccno[s]]=1;    while(!q.empty())    {        int k=q.front();        q.pop();        inq[k]=0;        for(int i=first[k];i!=-1;i=nxt[i])        {            int x=l[i].v;            if(dis[x]<dis[k]+l[i].w+val[x])            {                dis[x]=dis[k]+l[i].w+val[x];                if(!inq[x])                {                    q.push(x);                    inq[x]=1;                }            }        }    }}int main(){    memset(first,-1,sizeof(first));    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++)    {        scanf("%d%d%d%lf",&ru,&rv,&rw,&a);        build(ru,rv,rw,a);    }    scanf("%d",&s);    tot=0;    dfs(s);    num=0;    for(int i=1;i<=m;i++)    {        sum=0;        sum+=l[i].w;//若将l[i].w变为int型,则会产生误差,例:double 0.6实际为0.59999999999999998,与int相乘将下取整,而与double相乘则为与正常的0.6相乘        if(sccno[l[i].u]==0||sccno[l[i].v]==0)        continue;        if(sccno[l[i].u]==sccno[l[i].v])        {            int tmp=l[i].w;            while(tmp!=0)            {                tmp=tmp*l[i].b;                cout<<tmp<<endl;                sum+=tmp;            }            val[sccno[l[i].u]]+=sum;        }        else         Ed[++num]=sccno[l[i].u],As[num]=sccno[l[i].v],wws[num]=l[i].w,bs[num]=l[i].b;    }    memset(first,-1,sizeof(first));    memset(nxt,0,sizeof(nxt));    memset(l,0,sizeof(l));    tot=0;    for(int i=1;i<=num;i++)    build(Ed[i],As[i],wws[i],bs[i]);    SPFA();    for(int i=1;i<=cnt;i++)    maxn=max(dis[i],maxn);    printf("%d\n",maxn);    return 0;}

Problem 3 :

题目来源:https://www.luogu.org/problem/show?pid=1709

题目描述

众所周知, DQS 是一名大学生,正把自己投入到“享受大学生活” 这一热火朝天的工作中。
为了实现自己 AK 全场, 成为神犇,迎娶白富美,走上人生巅峰这一目标, DQS 决定先攻陷学校的学生会。首先他需要知道学生会的口令。哈工大的学生会有很奇怪的方法来隐藏他们的口令。 每届学生会长会选择一个字符串 S(由 L 个小写字母组成, 5<=L<=100,000),然后他把 S 顺时针绕成一个圈,每次取一个做开头字母并顺时针依次取字母而组成一个字符串。这样将得到一些字符串,他把它们排序后取出第一个字符串。把这个字符串的第一个字母在原字符串中的位置做为口令。第一个字母所在的位置是 0。
如字符串 alabala,按操作的到 7 个字符串,排序后得:
aalabal abalaal alaalab alabala balaala laalaba labalaa
第一个字符串为 aalabal,这个 a 在原字符串位置为 6,则 6 为口令。
因为 DQS 还有好多社团活动要参加,所以他请你帮他完成这件事。
输入描述
第一行:一个数: L
第二行:字符串: S
输出描述
一行,为得到的口令
样例输入
7
alabala
样例输出
6
数据范围及提示
对 30%的数据, n <= 100
对 50%的数据, n <= 10^5
对 100%的数据, n <= 10^6
对 70%的数据,保证字符串随机生成。
保证 S 都由小写字母组成。
思路
暴力比较,排除无用比较方案即可

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;int len;char s[1000010];int main(){    scanf("%d%s",&len,s);    /*int i=1,j=2,k=0;//从1开始存     while(i<=len&&j<=len&&k<=len)    {        int tmp1=i+k,tmp2=j+k;        if(tmp1>len)        tmp1-=(len);        if(tmp2>len)        tmp2-=(len);        int tmp3=s[tmp1]-s[tmp2];        if(tmp3==0)        k++;        else if(tmp3>0)        {            i=i+k+1;            k=0;        }        else if(tmp3<0)        {            j=j+k+1;            k=0;        }        if(i==j)        j++;    }*/    int i=0,j=1,k=0;    while(i<len&&j<len&&k<len)    {        int tmp1=i+k,tmp2=j+k;        if(tmp1>(len-1))//细节处理         tmp1-=len;        if(tmp2>(len-1))        tmp2-=len;        int tmp3=s[tmp1]-s[tmp2];        if(tmp3==0)        k++;        else if(tmp3>0)//若以i开头的字符串字典序较大,则从i向后k位都不可能作为最小答案的开端         {            i=i+k+1;            k=0;        }        else if(tmp3<0)        {            j=j+k+1;            k=0;        }        if(i==j)        j++;    }    printf("%d",min(i,j));//注意取min     return 0;}

Problem 4 :

题目来源:https://www.luogu.org/problem/show?pid=1984

题目描述

众所周知, Skyvot 是一名猫奴,正把自己投入到成为一名优秀的铲屎官这一热火朝天的工作中。
一天中午, 猫要喝热水, Skyvot 去找舍友借水壶。舍友却说: 如果你能回答上我的问题,我才能把水壶借给你。 下面是舍友的问题:“把总质量为 1kg 的水分装在 n 个杯子里,每杯水的质量均为(1/n)kg, 初温度均为 0℃。现需要把每一杯水都烧开。我们可以对任意一杯水进行加热。把一杯水的温度升高 t℃所需的能量为(4200*t/n)J,其中, “J” 是能量单位“焦耳” 。如果一旦某杯水的温度达到 100℃,那么这杯水的温度就不能再继续升高,此时我们认为这杯水已经被烧开。显然地,如果直接把水一杯一杯地烧开,所需的总能量为(4200*100)J。
在烧水的过程中,我们随时可以在两杯温=度不同的水之间进行热传递操作。热量只能从温度较高的那杯水传递到温度较低的那杯水。由于两杯水的质量相同,所以进行热传递操作之后,原来温度较高的那杯水所降低的温度总是等于原来温度较低的那杯水所升高的温度。
一旦两杯水的温度相同,热传递立刻停止。
为了把问题简化,我们假设:
1、没有进行加热或热传递操作时,水的温度不会变化。
2、加热时所花费的能量全部被水吸收,杯子不吸收能量。
3、热传递总是隔着杯子进行, n 杯水永远不会互相混合。
4、热传递符合能量守恒,而且没有任何的热量损耗。
在这个问题里,只要求把每杯水都至少烧开一遍就可以了,而不要求最终每杯水的温度都是 100℃。我们可以用如下操作把两杯水烧开:先把一杯水加热到 100℃,花费能量(4200*100/2)J,然后两杯水进行热传递,直到它们的温度都变成 50℃为止,最后把原来没有加热到 100℃的那杯水加热到 100℃,花费能量(4200*50/2)J,此时两杯水都被烧开过了,当前温度一杯 100℃,一杯 50℃,花费的总能量为(4200*75)J,比直接烧开所需的(4200*100)J 少花费了 25%的能量。
你的任务是设计一个最佳的操作方案使得 n 杯水都至少被烧开一遍所需的总能量最少。 ”
忙着给猫顺毛的 Skyvot 放心地把这件事托付给了你。
输入描述
输入文件只有一个数 n。
输出描述
输出 n 杯水都至少被烧开一遍所需的最少的总能量,单位为 J,四舍五入到
小数点后两位。
样例输入
2
样例输出
315000.00
数据范围及提示
对于 100%的数据 1≤n≤50000
The end.
思路
一杯水被烧开后,后续温度对答案再无印象,则尽量将之前被烧开的水的温度传导到未加热的水中,使热量利用最大化。
方法:以烧开的水按当前温度从低到高与即将加热的水接触,设沸腾温度a,通过传到得到的热量Ci,需要加热的温度Ti,则有:
第一杯
C1=0,T1=a
第二杯
C2=a/2,T2=a/2
第三杯
C3=(a/4+a)/2 T3=(3/8 )a
第四杯
C4=(((a/8+(a/4+a)/ 2)/ 2)+a)/2 T4=(5/16)*a
观察得:
T4/T3=5/6
T3/T2=3/4
T(n+1)/Tn=1-1/2n
递推计算即可。

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;int n;double t1,t2,ans;int main(){    scanf("%d",&n);    ans=100;//第一杯水的代价     t1=100;//初始温度     for(int i=2;i<=n;i++)//递推     {        t2=(1.0-(0.5/(i-1.0)))*t1;        ans+=t2;        t1=t2;    }    printf("%.2lf",4200*(ans/n));    return 0;}
原创粉丝点击