NOI 2018 屠龙勇士(exCRT)

25 篇文章 0 订阅

NOI 2018 屠龙勇士(exCRT)

题目链接

根据题目。对于每一条龙,设其血量为a,回血量为p。选择武器的攻击力为ATK,攻击的最少次数为x。可以得到同余方程:
A T K ⋅ x ≡ a ( m o d p ) ATK\cdot x\equiv a\pmod p ATKxa(modp)
这个方程也等价于
A T K g c d ( A T K , p ) ⋅ x ≡ a g c d ( A T K , p ) ( m o d p g c d ( A T K , p ) ) \frac{ATK}{gcd(ATK,p)}\cdot x\equiv \frac{a}{gcd(ATK,p)}\pmod {\frac{p}{gcd(ATK,p)}} gcd(ATK,p)ATKxgcd(ATK,p)a(modgcd(ATK,p)p)
把上面的同余方程转换为下面的好处是,由于模数的变小,让中间数据更不易溢出。

证明: a ⋅ x ≡ b ( m o d p ) a\cdot x\equiv b \pmod p axb(modp)等价于 a d ⋅ x ≡ b d ( m o d p d ) \frac{a}{d}\cdot x\equiv \frac{b}{d}\pmod {\frac{p}{d}} daxdb(moddp),其中 d = g c d ( a , p ) d=gcd(a,p) d=gcd(a,p)
根据扩展欧几里得算法的证明可知,方程 a ⋅ x ≡ b ( m o d p ) a\cdot x\equiv b \pmod p axb(modp)有解当且仅当 g c d ( a , p ) ∣ b gcd(a,p)|b gcd(a,p)b
若方程 a ⋅ x ≡ b ( m o d p ) a\cdot x\equiv b \pmod p axb(modp)无解,则显然另一个方程也是无解的。
假设方程有解,令 d = g c d ( a , p ) d=gcd(a,p) d=gcd(a,p),有
a ⋅ x ≡ b ( m o d p ) ⟺ a ⋅ x − b = y ⋅ p ⟺ a d ⋅ x − b d = y ⋅ p d ⟺ a d ⋅ x ≡ b d ( m o d p d ) \begin{aligned} a\cdot x\equiv b \pmod p &\Longleftrightarrow a\cdot x-b=y\cdot p\\ &\Longleftrightarrow \frac{a}{d}\cdot x-\frac{b}{d}=y\cdot \frac{p}{d}\\ &\Longleftrightarrow \frac{a}{d}\cdot x\equiv \frac{b}{d} \pmod {\frac{p}{d}} \end{aligned} axb(modp)axb=ypdaxdb=ydpdaxdb(moddp)
如此一来,整个题目就转换为一个含有n个同余方程的方程组。
{ a n s ≡ x 1 ( m o d p 1 ) a n s ≡ x 2 ( m o d p 2 ) . . . a n s ≡ x n ( m o d p n ) \left\{\begin{matrix} ans\equiv x_1 \pmod {p_1}\\ ans\equiv x_2 \pmod {p_2}\\ .\\ .\\ .\\ ans\equiv x_n \pmod {p_n} \end{matrix}\right. ansx1(modp1)ansx2(modp2)...ansxn(modpn)
通过扩展中国剩余定理就可以轻松求解啦。
需要注意的是,题目有个限制条件:对于每一条龙 a − A T K ⋅ x < 0 a-ATK\cdot x<0 aATKx<0
所以需要找出所有龙的x的最大值Max。在求得线性同余方程组的解X后,需要满足X>=Max。

因为同余方程的解是一个同余类,其符合解的同余类的代表元为X,我们只需要让 a n s = X + ⌈ M a x − X p ⌉ ⋅ P ans=X+\lceil\frac{Max-X}{p}\rceil\cdot P ans=X+pMaxXP即可。

至于在选择哪把剑🗡的问题,可以考虑用multiset(多重集合)来实现。multiset自带upper_bound函数,支持插入insert,删除erase。

由于中间数据会爆long long,在解同余方程时,选择用慢速乘来替代乘法。

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
const int max_n=1e5+5;
ll a[max_n];
ll p[max_n];
ll reward[max_n];
ll atk[max_n];
ll Max;
multiset<ll> ms;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
	if(!b){
		d=a;x=1;y=0;return ;
	}
	else{
		exgcd(b,a%b,d,y,x);y-=(a/b)*x;return ;
	}
}
ll mul(ll a,ll b,ll mod)
{
	a%=mod;
	ll ans=0;
	while(b)
	{
		if(b&1)ans=(ans+a)%mod;
		a=(a+a)%mod;
		b>>=1;
	}
	return (ans%mod+mod)%mod;
}
ll equation(ll a,ll b,ll &p)//a*x=b (mod p)
{
	a%=p;b%=p;
	ll x,y,d;
	exgcd(a,p,d,x,y);
	if(b%d)return -1;
	x=mul(x,b/d,p/d);
	p/=d;
	return x;
 } 
ll solve(void)
{
	ll X=0,P=1;
	for(int i=1;i<=n;i++)
	{
		ll t0=a[i]/atk[i]+(a[i]%atk[i]?1:0);//a[i]-x*atk[i]<0
		if(t0>Max)Max=t0;
		ll b=equation(atk[i],a[i],p[i]);//每个同余方程得解 
		if(b==-1)return -1;
		ll x,y,d;
		ll t=((b-X)%p[i]+p[i])%p[i];//EXCRT
		exgcd(P,p[i],d,x,y);
		if(t%d)return -1;
		x=mul(x,t/d,p[i]/d);
		X+=x*P;
		P*=p[i]/d; 
	}
	if(X>=Max)return X;
	else return X+((Max-X)/P+((Max-X)%P?1:0))*P;
}
int main(void)
{
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int t;
	ll t2;
	scanf("%d",&t);
	while(t--)
	{
		max=-1;
		ms.clear();
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
		for(int i=1;i<=n;i++)
		scanf("%lld",&p[i]);
		for(int i=1;i<=n;i++)
		scanf("%lld",&reward[i]);
		for(int i=1;i<=m;i++)
		{
			scanf("%lld",&t2);
			ms.insert(t2);
		}
		multiset<ll>::iterator it;
		for(int i=1;i<=n;i++)
		{
			it=ms.upper_bound(a[i]);
			if(it!=ms.begin())it--;
			atk[i]=*it;
			ms.erase(it);
			ms.insert(reward[i]);
		}
		printf("%lld\n",solve()); 
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值