「解题报告」[NOI2018]屠龙勇士 (EXCRT)

传送🚪

题意

题意有点复杂...懒得概括了...直接看题目吧...


思路

主要过程

首先, 注意到对付龙 \(i\) 的剑的攻击值 \(c_i\) 是一定的, 并可以预先求出来.

我们可以使用 \(std::muliset\) (可重集合) 来存当前有的剑, 并用 \(upper\_bound\) 求出 \(c_i\).

具体来说, 若 \(upper\_bound\) 返回的迭代器 \(iterator = S.begin()\), 则 \(c_i = *iterator\); 否则 \(c_i = *(--iterator)\).


求出 \(c_i\) 后, 我们可以列出一个关于 \(x\) 的线性同余方程组 (\(a < p\)),

\[\left\{ \begin{aligned} c_1 \cdot x &\equiv a_1 \pmod{p_1} \\ c_2 \cdot x &\equiv a_2 \pmod{p_2} \\ c_3 \cdot x &\equiv a_3 \pmod{p_3} \\ &\ \ \vdots \\ c_n \cdot x &\equiv a_n \pmod{p_n} \\ \end{aligned} \right. \]

第一反应是使用 EXCRT (扩展中国剩余定理), 但是这里 \(x\) 的系数不是 \(1\), 所以我们首先要对方程进行转化.

对于方程

\[c \cdot x \equiv a \pmod{p} \]

把它化为不定方程

\[c \cdot x + p \cdot y =a \]

用 exgcd 解出它的一个特解 \(sx\), 通解 \(x\) 即可表示为

\[x \equiv sx \pmod{\frac{p}{(c,p)}} \]

那么上述的方程组就可以化为

\[\left\{ \begin{aligned} x &\equiv sx_1 \pmod{\frac{p_1}{(c_1,p_1)}} \\ x &\equiv sx_2 \pmod{\frac{p_2}{(c_2,p_2)}} \\ x &\equiv sx_3 \pmod{\frac{p_3}{(c_3,p_3)}} \\ &\ \ \vdots \\ x &\equiv sx_n \pmod{\frac{p_n}{(c_n,p_n)}} \\ \end{aligned} \right. \]

然后我们就可以用 EXCRT 愉快地求解了.

特殊情况

1) \(a > p\)

根据题目数据, 当 \(a > p\) 时, 必定满足 \(p=1\). 也就是说, 只要我们把一条龙的生命值降为非正数, 则它一定会死亡, 所以答案 $x = \max_{i=1}^{n} \lceil \frac{a_i}{c_i} \rceil $.

2) \(\forall i, a_i =p _i\)

这时, 方程组形如

\[\left\{ \begin{aligned} c_1 \cdot x &\equiv 0 \pmod{p_1} \\ c_2 \cdot x &\equiv 0 \pmod{p_2} \\ c_3 \cdot x &\equiv 0 \pmod{p_3} \\ &\ \ \vdots \\ c_n \cdot x &\equiv 0 \pmod{p_n} \\ \end{aligned} \right. \]

那么最小非负整数解就是 \(0\), 显然这不符合题意, 所以我们在求解的时候要保证 \(x\) 是正数.


代码

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;

const int _=1e5+7;

int n,m;
ll a[_],p[_],c[_],t[_];
bool fl;
multiset<ll> S;

ll gi(){
	ll x=0; char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x;
}

void Init(){
	fl=0,S.clear();
	
	n=gi(),m=gi();
	for(int i=1;i<=n;i++)
		a[i]=gi();
	for(int i=1;i<=n;i++){
		p[i]=gi();
		if(a[i]>p[i]) fl=1;
	}
	for(int i=1;i<=n;i++)
		t[i]=gi();
	for(int i=1;i<=m;i++)
		S.insert(gi());

	for(int i=1;i<=n;i++){
		multiset<ll>::iterator pt=S.upper_bound(a[i]);
		c[i]= pt==S.begin() ?*pt :*(--pt);
		S.erase(pt),S.insert(t[i]);
	}
}

void Run1(){
	ll ans=0;
	for(int i=1;i<=n;i++){
		ll x= a[i]%c[i] ?a[i]/c[i]+1 :a[i]/c[i];
		ans=max(ans,x);
	}
	printf("%lld\n",ans);
}

ll Gcd(ll a,ll b){ return b ?Gcd(b,b%a) :a; }

ll Mul(ll a,ll b,ll p){ return ((ull)a*b-(ull)((ld)a/p*b)*p+p)%p; }		//快(gui)速乘, 防止溢出

ll Exgcd(ll a,ll b,ll &x0,ll &y0){
	if(!b){
		x0=1,y0=0;
		return a;
	}
	ll x1,y1,d=Exgcd(b,a%b,x1,y1);
	x0=y1;
	y0=x1-a/b*y1;
	return d;
}

bool Solve(ll a,ll b,ll c,ll &d,ll &sx){
	ll y0;
	d=Exgcd(a,b,sx,y0);
	if(c%d) return 0;
	sx=Mul(c/d,sx,b/d);
	return 1;
}

void Run2(){
	for(int i=1;i<=n;i++){
		ll d,sx;
		if(!Solve(c[i],p[i],a[i],d,sx)){ puts("-1"); return; }
		c[i]=sx,p[i]=p[i]/d;
	}

	for(int i=1;i<n;i++){
		ll d,k;
		if(!Solve(p[i],p[i+1],c[i+1]-c[i],d,k)){ puts("-1"); return; }
		ll lcm=p[i]/d*p[i+1];
		c[i+1]=(Mul(k,p[i],lcm)+c[i])%lcm,p[i+1]=lcm;
		if(!c[i+1]) c[i+1]=lcm;
	}
	printf("%lld\n",c[n]);
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("dragon2.in","r",stdin);
	freopen("dragon.out","w",stdout);
#endif
	int T=gi();
	while(T--){
		Init();
		if(fl) Run1();
		else Run2();
	}
	return 0;	
}
posted @ 2020-07-12 08:07  BruceW  阅读(163)  评论(0编辑  收藏  举报