嗯,是阳光明媚的一天。
这么好的天气怎么能不考点试呢?(正经脸▼-▼)
咳,不多说了,把题扔出来。
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
long long a[200010],n,m,t=0;
inline bool zql(long long x)
{
if(xreturn 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<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.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
#include
#include
#include
#include
#include
#include
#include
#include
#include
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.sinline 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<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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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]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<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,是一个时代的终结。