zoj 3836
Circulation pipe
题意:有L个盒子[0..L-1],每k秒在0号盒子放入一件物品,每一秒盒子里的物品向前移动一格,到最右边或最左边,物品反向运动,问最短多少时间内某个盒子中物品数量超过c。
分析 (1) 在a位置的物品满足运动时间为 (正向)a,a+T,a+2T,… 和(反向)T-a,2T-a,… (T=2*l-2)。
(2) 若有一物品正向运动到a位置,(这个物品一定是第一次到a位置最优),即有某个物品运动a时间运动至a位置。
若其他在a盒子的物品的运动时间t,则(t-a)%k==0。
(3) 基于(2)正向运动的物品 此时在a位置的有 t=a,a+T*k/gcd(T,k),a+2*T*k/gcd(T,k)…,
反向运动的物品 此时在a 位置的有 t=b,b+T*k/gcd(T,k),b+2*T*k/gcd(T,k)…
(4)(3)中的b是几呢,(1)中知 b=xT-a 且 (2)最后(b-a)%k==0
所以 ((xT-a)-a)%k==0 =>xT-yk==2a 中国剩余定理跑一下 (当然b可能无解 且 有隐含条件b>a)
(5) 大于c个 那么最少要在第几秒 (b无解,或者a==0||a==l-1自行考虑)
1个是a秒
2个是b秒,
3个是a+T*k/gcd(T,k)秒
4个是b+T*k/gcd(T,k)秒… 知道规律了
(6) 枚举每一个a位置求出这个位置出现大于c个球的最短时间,取最小的一个就可以了。
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
int l,k,c;
int T;
int exgcd(int a,int b,int &x,int &y){
int d=a;
if(b!=0){
d=exgcd(b,a%b,y,x);
y-=(a/b)*x;
}
else{x=1;y=0;}
return d;
}
long long f(int a){
long long b=-a;
int x,y;
int d=exgcd(T,k,x,y);
long long t=(T*k/d);
if((2*a)%d!=0) b=-1;
else{
x=x*(2*a)/d;
b=(long long)x*T-a;
b=(b%t+t)%t;
while(b<=a) b+=t;
}
if(b!=-1&&(b-a)%k!=0) while(1);
if(b==-1||a==0||a==l-1) return (c-1)*t+a;
if(c&1) return (c/2)*t+a;
else return (c/2-1)*t+b;
}
void solve(){
long long ans=1LL<<60;
for(int i=0;i<l;i++){
ans=min(ans,f(i));
}
printf("%lld\n",ans);
}
int main()
{
while(scanf("%d%d%d",&l,&k,&c)!=EOF){
if(l==1) {printf("%d\n",c*k);continue;}
c++;
T=2*(l-1);
solve();
}
return 0;
}