zoukankan      html  css  js  c++  java
  • 「SOL」屠龙勇士(LOJ)

    写题解当作复习笔记
    (这样就可以少写一篇博客了 Yeah)


    # 题面

    > Link LOJ 2721


    # exCRT

    「问题」

    求解线性同余方程组

    $$ egin{cases} xequiv a_1pmod{m_1}\ xequiv a_2pmod{m_2}\ cdots\ xequiv a_npmod{m_n} end{cases} $$

    $m$ 可以不互质。

    考虑等价地合并两个同余方程:

    [egin{cases} xequiv a_1pmod{m_1}\ xequiv a_2pmod{m_2} end{cases} ]

    可以把同余方程写为不定方程的形式,于是得到参数的一些关系:

    [x=k_1m_1+a_1=k_2m_2+a_2 ]

    由于 (m_1,m_2) 不一定互质,所以关于 (k_1,k_2) 的不定方程不一定有解。具体地说,设 ((m_1,m_2)=g),则

    [k_1m_1-k_2m_2=a_2-a_1 ]

    等式左侧 (gmid k_1m_1-k_2m_2),则右侧也必须满足 (gmid a_2-a_1),否则无解。

    若满足上述条件,则等式两边同时除以 (g) 仍然等价,即

    [egin{array}{c} m_1'= frac{m_1}{g},m_2'= frac{m_2}{g},t= frac{a_2-a_1}{g}\ k_1m_1'-k_2m_2'=t end{array} ]

    此时 ((m_1',m_2')=1),可以直接用 exGCD 解决上述问题,得到 (k_1) 的一个特解为 (k_0)。由

    [k_1m_1'-k_2m_2'=(k_1+m_2')m_1'-(k_2+m_1')m_2' ]

    可以知道 (k_1) 的通解为 (k_1=k_0+nm_2'(ninmathbb{Z}))

    (k_1) 代回 (x) 的表达式,则有

    [egin{array}{c} x=k_0m_1+a_1+nm_1m_2'\ xequiv k_0m_1+a_1pmod{m_1m_2'} end{array} ]

    于是就得到了两个式子合并后的等价的式子。这样不断合并就可以得到最终 (x) 的通解。


    # 解析

    一开始可以根据输入求出「用哪一把剑攻击第 (i) 条龙」,记为 (w_i)。具体可以用 multiset 实现。

    点击展开/折叠multiset使用技巧

    multiset 内部每个位置储存了一个元素(并不是把相同的元素合到同一个位置并且记录次数),因此用迭代器 iterator 访问其中的某个位置,访问到的是单个元素。如果按迭代器顺序访问 multiset,就相当于把插入的所有元素排了个序。

    multiset 内置有 lower_bound 和 upper_bound 函数,前者返回第一个大于等于给定值的元素的迭代器,后者返回第一个严格大于给定值的元素的迭代器。

    multiset 的 delete 函数有两类参数。第一类是给定数值,会删除其中所有该数值;第二类是给定迭代器,会删除迭代器对应的元素——这样就只会删掉一个数字

    利用这些特点,我们可以用 upper_bound 找到第一把攻击力大于龙的生命值的剑,而上一把剑就是攻击力小于等于生命值的剑。然后用 delete 删除该剑的迭代器(不能是数值!)。

    记龙的生命为 (h_i),回复力为 (r_i),则有两个限制:

    • 攻击后龙的生命模 (r_i)(0)
    • 攻击后,龙的生命小于等于 (0)

    问题直接转化为 (n) 对方程构成的方程组

    [egin{cases} w_ixequiv h_ipmod{r_i}\ w_ixge h_i end{cases} ]

    不等式方程可以求出 (x) 的下界。考虑如何求解同余方程。

    (w_i,r_i) 不一定互质,于是可能本身就无解。记 ((w_i,r_i)=g_i),则必须满足 (g_imid h_i),方程才有解。

    满足有解的条件后,(w_i,h_i,r_i) 同时除以 (g_i),得到等价的方程 (w_i'x=h_i'pmod {r_i'}),此时 ((w_i',r_i')=1)(w_i') 存在逆元,于是可以得到

    [xequiv (w_i')^{-1}h_i'pmod{r_i'} ]

    这个方程就可以用 exCRT 了。


    # 源代码

    点击展开/折叠代码
    /*Lucky_Glass*/
    #include<set>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    template<class T>T rin(T &r){
    	int b=1,c=getchar();r=0;
    	while(c<'0' || '9'<c) b=c=='-'?-1:b,c=getchar();
    	while('0'<=c && c<='9') r=(r<<1)+(r<<3)+(c^'0'),c=getchar();
    	return r*=b;
    }
    typedef long long llong;
    const int N=1e5+10;
    #define con(type) const type &
    
    multiset<llong> sword;
    int n,m;
    llong ih[N],rec[N],rew[N],atk[N];
    
    llong ina_GCD(con(llong)a,con(llong)b){return b?ina_GCD(b,a%b):a;}
    llong ex_GCD(con(llong)a,con(llong)b,llong &x,llong &y){
    	if(!b){x=1,y=0;return a;}
    	llong ret=ex_GCD(b,a%b,x,y);
    	swap(x,y);
    	y-=a/b*x;
    	return ret;
    }
    llong ina_ABS(con(llong)a){return a<0?-a:a;}
    llong mul(llong a,llong b,llong mod){
    	bool neg=(a<0)^(b<0);
    	a=ina_ABS(a),b=ina_ABS(b);
    	a%=mod,b%=mod;
    	llong ret=0;
    	while(b){
    		if(b&1) ret=(ret+a)>=mod? ret+a-mod:ret+a;
    		a<<=1,b>>=1;
    		if(a>=mod) a-=mod;
    	}
    	if(neg && ret) return mod-ret;
    	return ret;
    }
    pair<llong,llong> comb_CRT(con(llong)m0,con(llong)r0,con(llong)m1,con(llong)r1){
    	llong g=ina_GCD(m0,m1);
    	if((r1-r0)%g){
    		// printf("(%lld,%lld)=%lld %lld %lld
    ",m0,m1,g,r1,r0);
    		return make_pair(-1ll,-1ll);
    	}
    	llong p,q;
    	ex_GCD(m0/g,m1/g,p,q);
    	p=mul((r1-r0)/g,p,m1/g);
    	llong m2=m0/g*m1,r2=(mul(p,m0,m2)+r0)%m2;
    	if(r2<0) r2+=m2;
    	return make_pair(m2,r2);
    }
    llong solve(){
    	sword.clear();
    	rin(n),rin(m);
    	for(int i=1;i<=n;i++) rin(ih[i]);
    	for(int i=1;i<=n;i++) rin(rec[i]);
    	for(int i=1;i<=n;i++) rin(rew[i]);
    	for(int i=1,tmp;i<=m;i++) sword.insert(rin(tmp));
    	for(int i=1;i<=n;i++){
    		multiset<llong>::iterator it=sword.upper_bound(ih[i]);
    		if(it!=sword.begin()) it--;
    		atk[i]=*it;
    		sword.erase(it);
    		sword.insert(rew[i]);
    	}
    	llong mnbon=0;
    	pair<llong,llong> now(-1ll,-1ll);
    	for(int i=1;i<=n;i++){
    		mnbon=max(mnbon,(ih[i]+atk[i]-1)/atk[i]);
    		llong k=atk[i],r=ih[i],m=rec[i];
    		// printf("%lld x = %lld (mod %lld) -> ",k,r,m);
    		//kx = r (mod m)
    		k%=m,r%=m;
    		if(!k){
    			if(r){
    				// printf("A
    ");
    				return -1ll;
    			}
    			continue;
    		}
    		llong g=ina_GCD(k,m);
    		if(r%g){
    			// printf("B
    ");
    			return -1ll;
    		}
    		k/=g,m/=g,r/=g;
    		llong invk,non;
    		ex_GCD(k,m,invk,non);
    		invk=(invk%m+m)%m;
    		r=mul(r,invk,m);
    		// printf("x = %lld (mod %lld)
    ",r,m);
    		pair<llong,llong> tmp(m,r);
    		if(i==1) now=tmp;
    		else now=comb_CRT(now.first,now.second,tmp.first,tmp.second);
    		if(now.first==-1){
    			// printf("C
    ");
    			return -1ll;
    		}
    	}
    	if(now.first==-1) return mnbon;
    	if(now.second>=mnbon) return now.second;
    	else{
    		llong k=(mnbon-now.second+now.first-1)/now.first;
    		return now.second+k*now.first;
    	}
    }
    int main(){
    	// freopen("input.in","r",stdin);
    	freopen("dragon.in","r",stdin);
    	freopen("dragon.out","w",stdout);
    	int cas;rin(cas);
    	while(cas--) printf("%lld
    ",solve());
    	return 0;
    }
    

    THE END

    Thanks for reading!

    祈愿在风中渐渐冷冽
    那一刻她眼底有华光泯灭
    海潮呼啸着哽咽
    想把不舍宣泄
    所有分别都太决绝
    回忆都太炽烈
    最后一面何处去借

    ——《流光幻夜》By 司夏

    > Link 流光幻夜-网易云

    欢迎转载٩(๑❛ᴗ❛๑)۶,请在转载文章末尾附上原博文网址~
  • 相关阅读:
    ThinkPHP 实现数据库事务回滚示例代码
    Java数据结构和算法
    Java数据结构和算法
    Java数据结构和算法
    git push每次提交都要输入用户名的解决方案
    Java数据结构与算法
    Java数据结构和算法
    Java数据结构和算法
    类和接口
    git如何忽略文件
  • 原文地址:https://www.cnblogs.com/LuckyGlass-blog/p/14432835.html
Copyright © 2011-2022 走看看