洗衣服
【题目描述】
有 n n n 个人来到 B B BB BB家里洗衣服,第 i i i个人的到达时间是 t i t_i ti。
不幸的是, B B BB BB 家里只有一个洗衣机。
因此,同一时刻如果有多个人一起洗衣服的话,只能有一个人使用洗衣机,其他人只能手洗。
洗衣机洗一个人的衣服需要 x x x 的时间。
所有人手洗的速度是一样的,洗自己的衣服需要 y y y 的时间。
一个人的衣服不能在洗到一半的时候更换洗的方式。
给定 x x x 和 m m m,对于 y = 1 , 2 ⋯ , m y=1,2\cdots,m y=1,2⋯,m,分别求出所有人都洗完衣服的最早时刻。
【输入数据】
第一行三个整数 n n n, x x x, m m m。
第二行 n n n 个整数 t 1 − t n t_1 {-} t_n t1−tn。
【输出数据】
记 y = i y=i y=i 时答案为 a n s i ans_i ansi,输出一行一个整数 ( a n s 1 ) ⊕ ( 2 × a n s 2 ) ⊕ ( 3 × a n s 3 ) ⊕ ⋯ ⊕ ( m × a n s m ) (ans_1)\oplus(2\times ans_2)\oplus(3\times ans_3)\oplus\cdots\oplus(m\times ans_m) (ans1)⊕(2×ans2)⊕(3×ans3)⊕⋯⊕(m×ansm)
其中 ⊕ \oplus ⊕为计算机中的异或操作
【样例输入】
4 2 10
1 2 2 3
【样例输出】
113
【样例解释】
压缩前的答案为
4 5 5 6 7 8 8 9 9 9
【数据范围】
对于 5 % 5\% 5%的数据, n , m ≤ 10 n,m\leq 10 n,m≤10。
对于 25 % 25\% 25%的数据, n , m ≤ 1000 n,m\leq 1000 n,m≤1000。
对于 50 % 50\% 50%的数据, n ≤ 1 0 5 n\leq 10^5 n≤105。
对于另外 20 % 20\% 20% 的数据, n ≤ 1 0 6 n\leq 10^6 n≤106, m − x ≤ 20 m-x\leq 20 m−x≤20。
对于 100 % 100\% 100%的数据, 1 ≤ n , x , m ≤ 1 0 7 1\leq n,x,m\leq 10^7 1≤n,x,m≤107, 1 ≤ t i ≤ 1 0 9 1\leq t_i\leq 10^9 1≤ti≤109, t i ≤ t i + 1 t_i\leq t_{i+1} ti≤ti+1
对于此题,首先可以考虑一个人使用了洗衣机之后对后面的人造成的影响,但是会发现这样不太好维护,因为存在第 i − 1 i-1 i−1个人使用手洗时间比第 i i i个人机洗要慢的情况。
但是,如果知道了之后有多少个人会使用洗衣机,这样的话就能倒序计算出答案了。
第 n n n个人使用了洗衣机会让答案至少为 t n + x t_n+x tn+x,第 n − 1 n-1 n−1个人使用洗衣机会让答案至少为 t n − 1 + 2 x t_{n-1}+2x tn−1+2x, ⋯ \cdots ⋯,第 i i i个人使用了洗衣机会使答案至少为 t i + ( n − i + 1 ) x t_i+(n-i+1)x ti+(n−i+1)x。那么最终的答案就是 m a x ( t [ i ] + m i n ( y , ( n − i + 1 ) x ) ) max(t[i]+min(y,(n-i+1)x)) max(t[i]+min(y,(n−i+1)x))
但是直接枚举会使得复杂度为 O ( n m ) O(nm) O(nm)
考虑如何优化:
利用数学知识:
先将答案用分段函数的形式表达出来:
a
n
s
[
i
]
=
m
i
n
{
t
[
i
]
+
(
n
−
i
+
1
)
x
t
[
i
]
+
y
ans[i]=min \left\{\begin{aligned} t[i]+(n-i+1)x \\t[i]+y \end{aligned}\right.
ans[i]=min{t[i]+(n−i+1)xt[i]+y
将函数绘出可以得到此图:
将每一个不同的有关
y
y
y的分段函数绘出然后综合,对于每一个
y
y
y,最终函数图像上的最高点即为对应
y
y
y的答案。
对应的代码为:
for(int i=1;i<=n;i++)
{
t=read<int>();
ll tmp=1LL*(n-i+1)*x;
if(tmp>m)
{
ans[m]=max(ans[m],t+m);
}
else
{
ans[tmp]=max(1LL*ans[tmp],t+tmp);
}
}
求出最高点之后再想办法求出函数,可以采用标记左下方的点的方法
先倒序枚举:
for(int i=m;i>=1;i--)
{
ans[i]=max(ans[i+1]-1,ans[i]);
}
但是存在一种两个点在同一条斜率为零的线的情况:
画个图解释就是:
所以需要在再在最后统计答案的时候进行修正:
int k=ans[1];
for(int i=1;i<=m;i++)
{
k=max(k,ans[i]);
ans^=1LL*k*i;
}
然后就成功将 O ( n m ) O(nm) O(nm)的算法优化成了 O ( n + 2 m ) O(n+2m) O(n+2m)的
完整code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int twx=1e7+100;
const int inf=0x3f3f3f3f;
int n,x,m;
int t;
int ans[twx];
ll ANS;
char buf[1<<20],*p1,*p2;
#define getchar() ((p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
template<class T>
T read()
{
T r=0;
int w=0,c;
for(;!isdigit(c=getchar());r=c);
for(w=(c^48);isdigit(c=getchar());w=w*10+(c^48));
return r^45?w:-w;
}
void init()
{
n=read<int>();
x=read<int>();
m=read<int>();
for(int i=1;i<=n;i++)
{
t=read<int>();
ll tmp=1LL*(n-i+1)*x;
if(tmp>m)
{
ans[m]=max(ans[m],t+m);
}
else
{
ans[tmp]=max(1LL*ans[tmp],t+tmp);
}
}
for(int i=m;i>=1;i--)
{
ans[i]=max(ans[i+1]-1,ans[i]);
}
int k=ans[1];
for(int i=1;i<=m;i++)
{
k=max(k,ans[i]);
ANS^=1LL*k*i;
}
printf("%lld\n",ANS);
}
int main()
{
//freopen("wash.in","r",stdin);
//freopen("wash.out","w",stdout);
init();
return 0;
}