zoukankan      html  css  js  c++  java
  • 题解 Luogu P4774 [NOI2018]屠龙勇士

    题意

    (n) 只巨龙,第 (i) 只的初始生命值为 (a_i),并且拥有一个生命恢复值 (p_i)。你有 (m) 把剑,每把剑有一个攻击力。现在你要用这 (m) 把剑依次杀死这 (n) 只巨龙。

    在攻击第 (i) 只巨龙之前,你需要选出攻击力不大于 (a_i)攻击力最大的一把剑。如果这样的剑不存在,则选出攻击力最小的一把剑。设这把剑的攻击力为 (v),接下来:

    • 你会连续攻击巨龙 (x) 次,巨龙的生命值变为 (a_i -x imes v)
    • 巨龙会不断恢复生命值,每次恢复 (p_i),直到生命值非负;
    • 此时,若巨龙生命值为 (0),巨龙死去;
    • 用来攻击的这把剑会消失。与此同时,如果巨龙死去,你会获得一把新的剑,攻击力可能和这一把不同

    (x) 的最小非负整数解,或者指出 (x) 不存在。

    (1 leq n,m leq 10^5,p_i geq 1,operatorname{lcm}(p_i) leq 10^{12},a_i leq 10^{12})

    题解

    观察到用来攻击每个巨龙的剑的攻击力是固定的。设攻击第 (i) 只巨龙时,用到的剑攻击力为 (b_i)。考虑列出方程组

    [egin{aligned}b_1x equiv a_1 pmod{p_1} \ b_2x equiv a_2 pmod {p_2} \ cdots \ b_nx equiv a_npmod{p_n} end{aligned} ]

    考虑对方程进行化简,将形如 (b_ix equiv a_i pmod{p_i}) 的方程化为 (b_ix + p_iy = a_i) 的标准扩欧形式。

    首先判断该方程是否有解。根据裴蜀定理,该方程有解的充要条件是 (gcd(b_i,p_i) mid a_i)。使用 exgcd 求出原方程的一组特解 (x = x_0),那么 (x) 的解集为 (x = x_0 + k dfrac{p_i}{gcd(b_i,p_i)})。再化回同余方程的形式,得 (x equiv x_0 pmod{dfrac{p_i}{gcd(b_i,p_i)}})

    对于每个方程进行上述处理,便得到了可以使用 exCRT 的形式。设该方程组的最小非负整数解为 (s)。注意到 (s) 不一定是原问题的一个合法解,因为 (s) 次攻击可能不一定会使某条巨龙的生命值变为非正数。这个时候就需要将 (s) 不断增加 (operatorname{lcm}(dfrac{p_i}{gcd(b_i,p_i)}))(即新方程组模数的最小公倍数),直到 (s) 次攻击可以把所有巨龙的生命值变为非正数。

    # include <bits/stdc++.h>
    # define int __int128
    const int N=100010,INF=0x3f3f3f3f;
    
    int n,m;
    int a[N],p[N],b[N],c[N];
    std::multiset <int> S;
    
    int pval[N],bval[N];
    int maxx;
    
    inline int read(void){
    	int res,f=1;
    	char c;
    	while((c=getchar())<'0'||c>'9')
    		if(c=='-')f=-1;
    	res=c-48;
    	while((c=getchar())>='0'&&c<='9')
    		res=res*10+c-48;
    	return res*f;
    }
    void print(int x){
    	if(x<0)
    		putchar('-'),x=-x;
    	if(x>9)
    		print(x/10);
    	putchar(x%10+'0');
    	return;
    }
    inline int exgcd(int a,int b,int &x,int &y){
    	if(!b){
    		x=1,y=0;
    		return a;
    	}
    	int gcd=exgcd(b,a%b,x,y),Temp;
    	Temp=x,x=y,y=Temp-(a/b)*y;
    	return gcd;
    }
    inline bool build_excrt(void){
    	for(int i=1;i<=n;++i){
    		int A=c[i],B=p[i],C=a[i],xz,yz;
    		int gcd=exgcd(A,B,xz,yz);
    		if(C%gcd)
    			return false;
    		bval[i]=xz*C/gcd,pval[i]=B/gcd,bval[i]%=pval[i];
    	}
    	return true;
    }
    inline int solve(void){
    	if(!build_excrt()){
    		return -1;
    	}
    	int M=pval[1],ans=bval[1];
    	for(int i=2;i<=n;++i){
    		int A=M,B=pval[i],C=((bval[i]-ans)%pval[i]+pval[i])%pval[i],x,y,gcd;
    		gcd=exgcd(A,B,x,y);
    		if(C%gcd)
    			return -1;
    		ans+=C/gcd*x*M;
    		M=M/gcd*pval[i];
    		ans=(ans%M+M)%M;
    	}
    	if(ans<maxx)
    		ans+=((maxx-ans-1)/M+1)*M;
    	return ans;
    }
    signed main(void){
    	int T=read();
    	while(T--){
    		maxx=0;
    		S.clear(),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)
    			b[i]=read();
    		while(m--)	
    			S.insert(read());
    		for(int i=1;i<=n;++i){
    			std::multiset <int>::iterator it=S.upper_bound(a[i]);
    			if(S.begin()!=it)
    				--it;
    			c[i]=*it,S.erase(it),S.insert(b[i]);
    			maxx=std::max(maxx,(a[i]-1)/c[i]+1);
    		}
    		print(solve()),puts("");
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    table的使用
    Html标签
    mysql -4练习
    mysql -3练习(分组查询后再次筛选,顺逆序排序)
    mysql -2查询单个表的数据时添加各种条件
    mysql数据库简单练习(创建表格,增删改查数据)
    JS监视滚轮向上和向下滑动与滚轮距离上部的距离(转自网络)
    JavaScript (制造简易计算器)
    JavaScript-14(操纵属性和window)
    JavaScript-13(找到单选框的value值与复选框的全选操作)
  • 原文地址:https://www.cnblogs.com/liuzongxin/p/15256845.html
Copyright © 2011-2022 走看看