题解 P6515 【[QkOI#R1] Quark and Game】
首先,把这
可以发现,这两种操作对应了两个线性变换,把操作一和操作二对应的矩阵分别称作
在刚才转化后的题意中,似乎漏了一个只有当
接着,我们来考虑这个序列会漏掉哪些向量,也就是经过一系列变换后向量依旧在第一象限。比如空序列漏掉了第一象限的向量,只包含一个
然后,我们对这
- 当
\vec{v_1},\vec{v_2} 均在直线y=x 下面或直线上时,应当进行操作二(左乘M_2 ),若进行操作一,那么\vec{v_1},\vec{v_2} 将同时变得不可操作,此时漏掉向量的区域就会在\vec{v_2} 上方。 - 当
\vec{v_1},\vec{v_2} 均不在直线y=x 下面或直线上时,应当进行操作一(左乘M_1 ),若进行操作二,那就又回到了情况 1,但是情况 1 又只能再翻回来。 - 当
\vec{v_1},\vec{v_2} 中有恰好一个向量在y=x 直线下面或直线上时,只要进行操作一,那么就会使一个向量变得不可操作,所以我们要讨论一下操作一之前是否需要做一次操作二。假设不可操作的向量是\vec{v_1} ,我们做完操作一(这时使\vec{v_1} 不可操作)后再进行一次操作二,最后做若干次操作一使得\vec{v_2} 变得不可操作。注意这个时候我们让\vec{v_1} 不可操作后不能继续接着进行操作一,因为我们的目的是让漏掉向量的区域在这两个向量之间,所以我们必须先翻转,再进行操作一让\vec{v_2} 不可操作。这一步就是在原来的序列上左乘M_1^{t_1}M_2M_1 或者M_1^{t_2}M_2M_1M_2 ,其中t_1,t_2 是这种情况下让\vec{v_2} 不可操作的进行操作 1 的最小次数。
我们对每对相邻向量都进行这些讨论,最后选取花费最少的方案即可,时间复杂度是
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
struct pt
{
int a,b;
}c[100005];
int n;
ll ans,p,q;
bool cmp(const pt x,const pt y)
{
return 1ll*x.a*y.b>1ll*y.a*x.b; //使用叉积可以避免浮点运算
}
int main()
{
scanf("%d%lld%lld",&n,&p,&q);
for(int i=1;i<=n;i++)
scanf("%d%d",&c[i].a,&c[i].b);
sort(c+1,c+n+1,cmp);
ans=min(1ll*(c[n].b+c[n].a-1)/c[n].a*p,1ll*(c[1].a+c[1].b-1)/c[1].b*p+q); //特判
for(int i=1;i<n;i++)
{
pt u=c[i],v=c[i+1];
if(1ll*u.a*v.b==1ll*v.a*u.b) continue;
ll nw=0;
while(1)
{
if(u.a>=u.b&&v.a>=v.b) //case 1
{
swap(u.a,u.b),swap(v.a,v.b);
nw+=q;
}
else if(u.a<u.b&&v.a<v.b) //case 2
{
int ct=min((u.b-1)/u.a,(v.b-1)/v.a);
u.b-=ct*u.a,v.b-=ct*v.a;
nw+=ct*p;
}
else //case 3
{
if(u.a>=u.b) swap(u,v);
ll nw1=0,nw2=0;
pt w=u;
w.b-=w.a;
nw1=q+p+1ll*(w.a+w.b-1)/w.b*p; //case 3.1
if(v.a>v.b)
{
w=v;
swap(w.a,w.b);
w.b-=w.a;
nw2=p+q*2+1ll*(w.a+w.b-1)/w.b*p; //case 3.2
nw1=min(nw1,nw2);
}
nw+=nw1;
break;
}
}
ans=min(ans,nw);
}
printf("%lld",ans);
return 0;
}