嗯,是陽光明媚的一天。
這麼好的天氣怎麼能不考點試呢?(正經臉▼-▼)
咳,不多說了,把題扔出來。
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,是一個時代的終結。