2017.4.22考试

嗯,是阳光明媚的一天。
这么好的天气怎么能不考点试呢?(正经脸▼-▼)
咳,不多说了,把题扔出来。

1.装果子
果园里有 n 颗果树,每棵果树都有一个编号 i(1≤i≤n)。小明已经把每棵果树上的果子都摘下来堆在了这棵树的下方,每棵树下方的果子体积为 ai。现在小明将拿来 m 个袋子把这些果子都装进袋子里。每个袋子的体积为 v 。
小明会按照如下规则把果子装进袋子里:
(a) 从第 1 棵果树开始装起,由 1 到 n 一直装到第 n 棵果树。
(b) 如果这棵果树下的果子能全部装进当前这个袋子,就装进去;如果不能,就关上当前这个袋子,打开一个新的袋子开始装。
小明希望在能把所有果子都装进袋子里的前提下,v 尽量小。m 个袋子并不一定都要装进果子。

输入格式
输入第 1 行,包含两个整数 n 和 m 。
输入第 2 行,包含 n 个整数 ai 。

输出格式
输出仅一行,表示最小的 v 。

输入 
3 3
1 2 3
输出
3

【样例解释】
每个袋子的体积为 3 即可。前 2 棵果树的果子装在第一个袋子里,第 3 棵果树的果子装在第二个袋子里。第三个袋子不用装了。

扫一眼,嗯,还算是比较简单的二分。
但是数据……
对于100%的数据,n≤100,000,ai≤1,000,000,000。
于是忘了开long long的后果是毁灭性的。。。QAQ
那,放一只代码。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

long long a[200010],n,m,t=0;

inline bool zql(long long x)
{
    if(x<t)
        return 0;
    long long cnt=1,ans=0;
    for(long long i=1;i<=n;i++)   //对比。
        if((ans+=a[i])>x)
            ans=a[i],cnt++;
    return cnt<=m;
}

int main()
{
    //freopen("fruit.in","r",stdin);
    //freopen("fruit.out","w",stdout);

    cin>>n>>m;
    long long l=0,r=1ll*(1e14),mid;   //给r了一个很大的值做上界。 
    for(long long  i=1;i<=n;i++)
    {
        cin>>a[i];
        t=max(t,a[i]);
    }
    while(l<=r)
    {
        if(zql(mid=l+r>>1))    //其实不打mid=(l+r)的括号也可以。+的优先级高于>>。
            r=mid-1;
        else 
            l=mid+1;
    }
    cout<<l<<endl;
    return 0;
}

(这么简单却死在数据上……泪目)

很好,第二题。
2.零件加工
工匠小 K 最近有 n 个零件需要加工。每个零件都需要 ti 天的时间来完成,每个零件每延迟一天加工都要缴纳一定的罚金 si 。延迟的天数为从今天算起到该工作开始的那天,第一个零件加工没有罚金。现在小 K 想知道怎样安排加工顺序可以使他要交的罚金最少,最少是多少?
这个数可能会很大,请输出这个数对 m 取模后的结果。

输入格式
输入文件第一行为一个整数 n ,表示需要加工的零件总数。
第二行为一个整数 m ,表示答案要对 m 取模。
第 3~n+2 行,每行两个整数 ti 和 si。

输出格式
输出仅一行,一个整数,表示小 K 最少要缴纳的罚金对 m 取模的结果。

样例数据
输入 
2
100
2 33
33 2
输出
4

【样例解释 】
先加工第一个,需要 2 天时间,再加工第二个。需要缴纳的罚金为 2×2=4 。

当时看完题第一个想到的就是贪心。
但是……当敲到排序的时候真是整个人都不好了。。。
嗯,其实可以把t与s的商的排序改换成积的排序。

inline bool comp(const node &a,const node &b)
{
    return a.t*b.s<a.s*b.t;
}

还有,算后面的高精度相乘时,可以巧妙且完美的避开高精度乘法。

inline long long zql(long long a,long long b)
{
    long long r;
    for(r=0;b;a=(a<<1)%m,b>>=1)   //二进制计算所交罚金。 
        if(b&1)
            r=(r+a)%m;
    return r;
}

咳咳,如果崩溃的话,不妨看一个差不多但简约版的。

inline long long zql(long long a,long long b)
{
    long long ret=0;
    while(a)
    {
        if(a&1)       //如果a是奇数
            ret=(ret+b)%m;  //就把ret加一个b。
        a>>=1;         //a除以2
        b=(b<<1)%m;    //b再乘2(其实积不变,不过把乘法慢慢转换成加法)
    }
    return ret;
}

喏,完整的原版

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

struct node
{
    long long t;
    long long s;
}a[100003];
long long n,ans=0,tt=0;
long long m;

inline bool comp(const node &a,const node &b)
{
    return a.t*b.s<a.s*b.t;
}

inline long long zql(long long a,long long b)
{
    long long ret=0;
    while(a)
    {
        if(a&1)
            ret=(ret+b)%m;
        a>>=1;
        b=(b<<1)%m;
    }
    return ret;
}

int main()
{
    freopen("process.in","r",stdin);
    freopen("process.out","w",stdout);

    cin>>n;
    cin>>m;
    for(long long i=1;i<=n;i++)
        cin>>a[i].t>>a[i].s;
    sort(a+1,a+n+1,comp);
    for(long long i=2;i<=n;i++)
    {
        tt=(tt+a[i-1].t)%m;
        ans=(ans+zql(tt,(a[i].s%m)))%m;
    }
    cout<<ans<<endl;
    return 0;
}

嗯嗯,那么第三题。
3.种树
为了绿化乡村,H 村积极响应号召,开始种树了。
H 村里有 n 幢房屋,这些屋子的排列顺序很有特点,在一条直线上。于是方便起见,我们给它们标上 1~n 。树就种在房子前面的空地上。
同时,村民们向村长提出了 m 个意见,每个意见都是按如下格式:希望第 li 个房子到第 ri 个房子的房前至少有 ci 棵树。
因为每个房屋前的空地面积有限,所以每个房屋前最多只能种 ki 棵树。
村长希望在满足村民全部要求的同时,种最少的树以节约资金。请你帮助村长。

输入格式
输入文件输入第 1 行,包含两个整数 n,m 。
第 2 行,有 n 个整数 ki。
第 2~m+1 行,每行三个整数 li,ri,ci 。

输出格式
输出 1 个整数表示在满足村民全部要求的情况下最少要种的树。村民提的要求是可以全部满足的。

样例数据
输入 
5 3
1 1 1 1 1
1 3 2
2 4 2
4 5 1
输出
3

【样例1解释】
如图是满足样例的其中一种方案,最少要种 3 棵树。
(假装这里有图的样子,好吧,是图复制不过来)

这么裸的差分约束系统实在是太少见了。。。。
于是收获了一个圆满的Accepted。
直接上代码。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
using namespace std;

const int N=500010;
struct node
{
    int to;
    int next;
    int w;
}e[N*4];
int n,m,tot;
int head[N],a[N],dis[N];
bool flag[N];

inline int init()
{
    int p=0;
    char c=getchar();
    if(c>'9'||c<'0')
        c=getchar();
    while(c<='9'&&c>='0')
    {
        p=p*10+c-'0';
        c=getchar();
    }
    return p;
}

inline void czh(int u,int v,int w)
{
    tot++;
    e[tot].to=v;
    e[tot].w=w;
    e[tot].next=head[u];
    head[u]=tot;
}

inline void zql()
{
    queue<int>q;
    memset(dis,-110,sizeof(dis));
    dis[0]=0;
    flag[0]=1;
    q.push(0);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        flag[u]=0;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(dis[v]<dis[u]+e[i].w)
            {
                dis[v]=dis[u]+e[i].w;
                if(!flag[v])
                {
                    q.push(v);
                    flag[v]=1;
                }
            }   
        }
    }   
}

int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);

    int x,y,z;
    n=init();
    m=init();
    for(int i=1;i<=n;i++)
        a[i]=init();
    for(int i=1;i<=m;i++)
    {
        x=init();
        y=init();
        z=init();
        czh(x-1,y,z);
    }
    for(int i=1;i<=n;i++)
    {
        czh(i-1,i,0);
        czh(i,i-1,-a[i]);
    }
    zql();
    cout<<dis[n]<<endl;
    return 0;
}

PS. 关于建边的一些碎碎念
很容易得出,满足题目需要满足下面三个方程:
dis[r]-dis[l-1]>=c,
s[i]>=s[i-1],
s[i]-s[i-1]<=k.
则在r和l-1之间连一条边权为-c的边。
在i和i-1之间连一条边权为0的边。
在i-1和i之间连一条边权为k的边。
(为什么一定要这样连?记住要满足最短路径的方程。)

呐,阳光依旧挺好的,又是一周。
夏日静好^-^

——我认为return 0,是一个时代的终结。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章