zoukankan      html  css  js  c++  java
  • NOI2018Day2T1 屠龙勇士 set 扩展欧几里德 中国剩余定理

    原文链接https://www.cnblogs.com/zhouzhendong/p/NOI2018Day2T1.html

    题目传送门 - 洛谷P4774

    题意

    题解

      首先我们仔细看一看样例可以发现如果一回合打不过巨龙就输了。

      所以每一回合都要赢。所以每一次选择的宝剑都是可以提前预知的。

      我们用个 set 来支持快速插入和 upper_bound ,可以在 $O((n+m)log m)$ 的时间复杂度内处理得到每一把宝剑要处理的巨龙。

      我们考虑化简一下原题的意思:

      令 $v_i$ 为攻击第 $i$ 只龙的宝剑的攻击力。

      则对于最终答案 $x$ 必然满足:

    $$forall iin {1,2,cdots,n}$$

    $$a_ileq x imes v_i$$

    $$a_iequiv x imes v_ipmod {p_i}$$

      我们先考虑第二个条件。

    $$a_iequiv x imes v_ipmod {p_i}Longrightarrow xequiv cfrac{a_i}{v_i}pmod {p_i}$$

      于是我们需要求出 $(v_i)^{-1} pmod {p_i}$ 。

      但是 $v_i$ 的逆元在对 $p_i$ 取模意义下不一定存在,不存在的条件是 $gcd(v_i,p_i)>1$ 。这个求逆元一般方法自己百度。

      但是即使不存在,也有可能使得 $x imes v_iequiv a_i$ 。

      我们来看一看原式的本质:

    $$a_i=x imes v_i + k imes p_i$$

      令 $g=gcd(a_i,v_i,p_i)$ ,上式中的 $a_i,v_i,p_i$ 都除以 $g$ ,上式依旧成立。

      接下来,上述 $a_i,v_i,p_i$ 的值都更新成他们除以 $g$ 的值。

      此时,如果 $g2=gcd(v_i,p_i)>1$ ,那么由于 $gcd(g2,a_i)=1$ ,所以有 $x imes v_i + k imes p_i = M imes g2 ot equiv a_i pmod {g2 imes (p_i÷g2)}$ ,其中 $M=(x imes v_i + k imes p_i)÷g2$ 。

      那么显然无解了。

      如果 $g2=1$ ,那么显然有解。

      于是我们依照上述做法,可以判除掉一部分无解的情况,并得到关于 $x$ 的一次同余方程组。

      令 $x_i=a_i imes (v_i)^{-1}pmod {p_i}$ ,则:

    $$egin{cases}x&equiv&x_1&pmod {p_1}\x&equiv&x_2&pmod {p_2}\ &&vdots\x&equiv&x_n&pmod {p_n}end{cases}$$

      这个东西直接中国剩余定理合并一下(可能会无解)就可以得到 $xequiv W pmod P$ 这样的一般式子了。由于本题数据范围比较大,我用了快速乘来防止炸 $long long $ 。

      得到 $x$ 的一般同余式子之后,我们再去看看之前的第一个条件。

    $forall iin{1,2,cdots,n}, a_ileq x$

      由于 $x=kP + W$ ,所以我们可以将 $x$ 代入上面的式子中,并根据所有的式子求出 $k$ 的取值范围。于是就可以得到 $x$ 的最小值了!

      时间复杂度 $O(nlog n)$ 。

      期望得分 : 100 

      UPD(2018-07-20  22:16): 洛谷测试 :100

      UPD(2018-07-21): LOJ测试 :100

      UPD(2018-07-22): 评测鸭测试: 100

      UPD(2018-07-24): UOJ测试 : 100

      UPD(2018-xxxxx): xxx测试:100

      实际得分 : 75 (该分数为推测结果( Day2 少了 25 分大概只有可能是这里了))

      吐槽啊!CCF 老爷机跑的也太慢了吧??是不是没有开 O2 啊?

    代码

    #include <algorithm>
    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    #include <cstring>
    #include <vector>
    #include <set>
    #include <queue>
    using namespace std;
    typedef long long LL;
    bool isd(char ch){
    	return '0'<=ch&&ch<='9';
    }
    LL read(){
    	LL x=0;
    	char ch=getchar();
    	while (!isd(ch))
    		ch=getchar();
    	while (isd(ch))
    		x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    	return x;
    }
    const int N=100005;
    int T,n,m;
    LL a[N],p[N],h[N],v[N],x[N];
    multiset <LL> S;
    LL ex_gcd(LL a,LL b,LL &x,LL &y){
    	if (!b){
    		x=1,y=0;
    		return a;
    	}
    	LL res=ex_gcd(b,a%b,y,x);
    	y-=x*(a/b);
    	return res;
    }
    LL gcd(LL a,LL b){
    	return b?gcd(b,a%b):a;
    }
    LL inv(LL v,LL p){
    	LL x,y,g=ex_gcd(v,p,x,y);
    	if (g>1)
    		return -1;
    	return (x+p)%p;
    }
    LL Mul(LL a,LL b,LL p){
    	a=(a%p+p)%p;
    	b=(b%p+p)%p;
    	LL ans=0;
    	for (;a;a>>=1,b=(b<<1)%p)
    		if (a&1LL)
    			ans=(ans+b)%p;
    	return ans;
    }
    bool CRT(LL w1,LL p1,LL w2,LL p2,LL &w,LL &p){
    	LL x,y,z=w2-w1,g=ex_gcd(p1,p2,x,y);
    	if (z%g)
    		return 0;
    	LL t=z/g;
    	x=Mul(x,t,p2/g);
    	p=p1/g*p2;
    	w=((w1+Mul(x,p1,p))%p+p)%p;
    	return 1;
    }
    LL Solve(){
    	n=read(),m=read();
    	for (int i=1;i<=n;i++)
    		a[i]=read();
    	for (int i=1;i<=n;i++)
    		p[i]=read();
    	for (int i=1;i<=n;i++)
    		h[i]=read();
    	S.clear();
    	while (m--)
    		S.insert(read());
    	for (int i=1;i<=n;i++){
    		multiset <LL> :: iterator p=S.begin();
    		if ((*p)<a[i])
    			p=--S.upper_bound(a[i]);
    		v[i]=*p,S.erase(p);
    		S.insert(h[i]);
    	}
    	for (int i=1;i<=n;i++){
    		LL g=gcd(a[i],gcd(v[i],p[i]));
    		v[i]/=g,p[i]/=g,a[i]/=g;
    		LL Inv=inv(v[i],p[i]);
    		if (Inv<0)
    			return -1LL;
    		x[i]=Mul(a[i],Inv,p[i]);
    	}
    	LL W=x[1],P=p[1];
    	for (int i=2;i<=n;i++)
    		if (!CRT(W,P,x[i],p[i],W,P))
    			return -1LL;
    	// x = W  (mod P)
    	for (int i=1;i<=n;i++){
    		LL val=(a[i]+v[i]-1)/v[i];
    		if (val<=W)
    			continue;
    		LL k=(val-W+P-1)/P;
    		W+=k*P;
    	}
    	return W;
    }
    int main(){
    	freopen("dragon.in","r",stdin);
    	freopen("dragon.out","w",stdout);
    	scanf("%d",&T);
    	while (T--)
    		printf("%lld
    ",Solve());
    	fclose(stdin);fclose(stdout);
    	return 0;
    }
    

      

  • 相关阅读:
    GitHub 优秀的 Android 开源项目
    Android SDK代理服务器解决国内不能更新下载问题
    python拓展3 常用算法
    HTML+CSS实现页面
    数据库入门4 结构化查询语言SQL
    2016年蓝桥杯预选赛试题(水题)
    linux操作系统3 vi编辑器
    python应用之爬虫实战2 请求库与解析库
    python应用之爬虫实战1 爬虫基本原理
    SQL常用条件操作符
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/NOI2018Day2T1.html
Copyright © 2011-2022 走看看