2017.10.18 队内胡策 D5

昨天考的题,今天早晨才把T2调完,竟然卡了精度

原题链接:
T1:gjh自己出的(灵感来源:codevs 1742 爬楼梯 链接
T2:luogu 2656 采蘑菇 链接
T3:codevs 1456 隐藏口令 链接
T4:luogu 1984 烧水问题 链接

T1
这里写图片描述

第一问DP,第二问随便打打贪心
结果我第二问贪心在模拟的过程中就打次了(第一次贪心模拟输给了DP)

不多说 很简单
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=100000+10,mo=19260817;
int n,m;
int h[maxn],f[maxn],ans,cnt;

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i) 
    {
        int x,y;
        scanf("%d%d",&x,&y);
        h[x]=y;
    }
    for(int i=1;i<=n;++i)
      if(!h[i]) h[i]=1;
    f[1]=f[0]=1;
    cnt=h[1];
    ans=1;
    for(int i=2;i<=n;++i)
    {
        if(cnt+h[i]<=4) cnt+=h[i];
        else 
        {
            cnt=h[i];
            ans++;
        }
        int last=h[i];
        int j;
        for(j=i-1;j>=0;--j)
        {
            last+=h[j];
            f[i]=(f[i]%mo+f[j]%mo)%mo;
            if(last>4) break;
        }
    }
    printf("%d %d",f[n],ans);
    return 0;
}

T2:(模板题)
这里写图片描述

思路:tarjan缩点,处理每一个scc里最大的蘑菇数量,跑最长路

现在说一下我哪里被卡了….

在统计每一个scc里面的蘑菇数量时,对于每一条边,都要反复采集直到数量变为0

一开始,我的程序是这样写的:

int cal(int i)
{
    int f=e[i].f,t=e[i].t;
    int v=e[i].v;
    int ret=v;
    double k=e[i].k;
    while(v)
    {
        ret+=v*k;
        v*=k;
    }
    return ret;
}

死活wa了4个点,大写的懵逼

后来我发现这样写可以过:

int cal(int i)
{
    int f=e[i].f,t=e[i].t;
    int v=e[i].v;
    int ret=v;
    double k=e[i].k;
    while(v)
    {
        v*=k;
        ret+=v;
    }
    return ret;
}

然后就更懵逼了
(内心:这俩程序有什么不同??!!)
我把两个程序单独摘出来对拍。结果真拍出了一组不同的结果:
输入:6840 0.7
输出:第一种:21555 第二种:21554

我把两个程序的计算流程输出,发现有一个地方出现了问题:
中间有一个过程,需要答案+(20*0.7)
第一个程序,ans加了14,第二个,ans加了13
两段程序的区别就是:
前者直接把ans加上v*k,后者是先把v*k赋值给一个int型变量,再把ans加上这个int变量。

后来知道20*0.7=14.0,double中下取整会变成13.0,但是始终不知道这两种加法到底有什么区别,按理说计算结果都应该是ans+=13

不过至少知道了14.0这种东西是以13.99999..存储的,想把它下取整变成14的话,就加0.00005之类的

因为这个调了很久

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<vector>
#include<queue>
using namespace std;

const int maxn1=80000+500,maxn2=200000+500;
int n,m,cnt,s,scc_cnt,Index,ans;
int fist[maxn1],nxt[maxn2],low[maxn1],dfn[maxn1],where[maxn1],num[maxn1];
int fi[maxn2],ti[maxn2],vi[maxn2],dis[maxn1];
double ki[maxn2];
vector<int>which[maxn1];
bool instack[maxn1],vis[maxn1];
struct hh
{
    int f,t,v;
    double k;
}e[maxn2];
queue<int>q;
stack<int>S;

void init()
{
    memset(fist,-1,sizeof(fist));
    memset(nxt,0,sizeof(nxt));
    memset(dfn,-1,sizeof(dfn));
    memset(vis,0,sizeof(vis));
    cnt=0;
}
void build(int f,int t,int v,double k)
{
    e[++cnt]=(hh){f,t,v,k};
    nxt[cnt]=fist[f];
    fist[f]=cnt;
}
int cal(int i)
{
    int f=e[i].f,t=e[i].t;
    int v=e[i].v;
    int ret=v;
    double k=e[i].k;
    while(v)
    {
        v*=k;
        ret+=v;
    }
    return ret;
}
void tarjan(int i)
{
    dfn[i]=low[i]=++Index;
    S.push(i);
    for(int x=fist[i];x!=-1;x=nxt[x])
    {
        int j=e[x].t;
        if(dfn[j]==-1)
        {
            tarjan(j);
            low[i]=min(low[i],low[j]);
        }
        else if(!where[j]) low[i]=min(low[i],dfn[j]);
    }
    if(dfn[i]==low[i])
    {
        scc_cnt++;
        int j;
        do
        {
            j=S.top();
            S.pop();
            which[scc_cnt].push_back(j);
            where[j]=scc_cnt;
        }
        while(j!=i);
    }
}
void done()
{
    for(int u=1;u<=n;++u)
    {
        for(int i=fist[u];i!=-1;i=nxt[i])
        {
            int v=e[i].t;
            if(where[v]==where[u])
              num[where[v]]+=cal(i);
        }
    }
    init();
    for(int i=1;i<=m;++i)
    {
        int f=fi[i],t=ti[i];
        if(where[f]!=where[t])
          build(where[f],where[t],vi[i],ki[i]);
    }
}
void spfa(int ds)
{
    vis[ds]=true;
    q.push(ds);
    dis[ds]=num[ds];
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=false;
        for(int i=fist[u];i!=-1;i=nxt[i])
        {
            int v=e[i].t;
            if(dis[v]<dis[u]+e[i].v+num[v])
            {
                dis[v]=dis[u]+e[i].v+num[v];
                //e[i].v*=e[i].k;
                if(!vis[v])
                {
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&fi[i],&ti[i],&vi[i]);
        cin>>ki[i];
        build(fi[i],ti[i],vi[i],ki[i]);
    }
    scanf("%d",&s);
    for(int i=1;i<=n;++i)
      if(!where[i]) tarjan(i);
    done();
    spfa(where[s]);
    for(int i=1;i<=scc_cnt;++i) ans=max(ans,dis[i]);
    printf("%d",ans);
    return 0;
}

T3:
这里写图片描述
考试的时候没想到
其实直接两两比较就可以,两个指针,i指当前找到的字典序最小的串的开头字符,j指将要和它进行比较的字符串的开头字符,每次指针往后移,再用一个k每次比较两个指针往后k位的字符就可以了
一开始想的是:(伪代码)

if(s[i+k]>s[j+k]) 说明当前比较的字符串比之前的最小字符串更优,所以  i=j,j++;
if(s[i+k]<s[j+k]) 说明当前字符串不如之前的最小字符串更优,所以  j++  比较下一位

但是会TLE,而且TLE了65%
考虑优化
首先,如果每次指针j都往后只跳一个格子,肯定有冗杂的操作
对于s[i+k]>s[j+k]的情况,我们可以直接把j跳到i+k+1的地方,也就是说,对于以[i~i+k+1)这段区间,一定不会是答案。(如果j > i+k+1,直接跳到j,因为我们要保证i始终在j的左边,便于统计答案)
这里不是很好想。
来一波证明
即证明:对于以[i~i+k+1)这段区间开头的字符串,一定存在某字符串的字典序比它小
设该区间中某一个字符i’,则对于字符串s[i’~i+k],长度len为i+k-i’+1,则在区间[j~j+k+1),长度相同的以j’开头的字符串s[j’~j+k],由于这两段区间在比对到i+k和j+k之前比对时,已经确定,s[i’~i+k-1]一定等于s[j’~j+k-1],且s[i+k]>s[j+k],那么s[i’~i+k]的字典序一定大于s[j’~j+k],所以s[i’~i+k]一定不是答案。

对于s[i+k]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int n,ans;
string ss,s;

int main()
{
    scanf("%d",&n);
    while(cin>>ss) s+=ss;
    s+=s;
    int i=0,j=1;
        while(i<n&&j<n)
    {
        bool flg=0; 
        for(int k=0;k<n;++k)
        {
            if(s[i+k]>s[j+k]) 
            {
                int x=i;
                i=j;
                j=max(j,x+k)+1;
                break;
            }
            else if(s[i+k]<s[j+k])
            {
                j=j+k+1;
                break;
            }
            if(k==n-1)
            {
                flg=1;
                break;
            }
        }
        if(flg) break;
    }
    printf("%d",i);
    return 0;
}

T4:
这里写图片描述
这里写图片描述
数学推理题,代码贼短,懒得写了,直接贴题解吧╮(╯▽╰)╭

最大的情况就是使已经烧开的水的热量被尽可能的利用。 我们发现,当一杯水的热量一个个的往右传递下去的话,热量最不容易浪费。

热量的传递 实际数据解释: 假设有5杯水: 0 0 0 0 0

第一杯水: 100 0 0 0 0 –> 6.25 50 25 12.5 6.25 第二杯水: 6.25 100 25 12.5
6.25–> 6.25 21.875 62.5 37.5 21.875 第三杯水: 6.25 21.875 100 37.5 21.875–>6.25 21.875 45.3125 68.75 45.3125 第四杯水: 6.25 21.875 45.3125 100 45.3125–> 6.25 32.875 45.3125 72.65625 72.65625 第五杯水:…… 100 。

我们发现 这五杯水被烧开前只进行热传递可以达到的温度为 0 50 62.5 68.75 72.65625 还需要升高的温度为: 100
50 37.5 31.25 27.34375 发现: 50/100=1/2 、37.5/50=3/4
、31.25/37.5=5/6、27.34375/31.25=7/8 规律:第i杯水需要上升的温度为第i-1杯水需要上升的温度*
(2*(i-1)-1)/(2*(i-1)).
**热量的传递 公式解释(摘自洛谷题解) :

推导:设沸腾温度为a //则第一杯温度为a,需要加热t1=a //第二杯可以中和的最高温度为a/2,需要加热t2=a/2
//第三杯可以中和的最高温度为t3=(a/4+a)/2=5a/8,需要加热t3=3a/8
//第四杯可以中和的最高温度为t4=((a/8+5a/8)/2+a)/2=11a/16,需要加热t4=5/16
//则t3/t2=3/4=1-1/4, t4/t3=5/6=1-1/6 //继续推导得t(n+1)/t(n)=1-1/2n;

最后递推求解。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

int n;
double a=4200,k,ans=0,x=100;

int main()
{
    freopen("water.in","r",stdin);
    freopen("water.out","w",stdout);
    scanf("%d",&n);
    k=(double) 1/n;
    ans+=a*x*k;
    for(int i=2;i<=n;++i)
    {
        x*=(double)(2*(i-1)-1)/(2*(i-1));
        ans+=a*x*k;
    }
    printf("%.2lf",ans);
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值