BZOJ5418[Noi2018]屠龙勇士——exgcd+扩展CRT+set
题目链接:
题目大意:有$n$条龙和初始$m$个武器,每个武器有一个攻击力$t_{i}$,每条龙有一个初始血量$a_{i}$和一个回复值$p_{i}$(即只要血量为负数就一直回复$p_{i}$的血量,只有在攻击后会回血),杀死一条龙当且仅当攻击结束后或回复血量之后血量为$0$,杀死一条龙会获得一个新的武器。现在要求对每条龙攻击固定次数$x$求出最小的$x$,使所有龙都能被杀死。
因为每次选择的武器是固定的,所以只要用$multiset$存当前剩下的武器然后每次按题目规则取即可。设攻击第$i$条龙的武器攻击力为$ti$,那么可以得到$n$个不定方程$x*t_{i}-k*p_{i}=a_{i}$。对于每个不定方程因为$p_{i}$与$t_{i}$不一定互质,所以求出$d=gcd(p_{i},t_{i})$并将等式两边都除掉$d$(如果$a_{i}$不能整除$d$则无解)。这样每个方程就能用$exgcd$解出最小的非负整数解$x_{i}$,那么显然$x\equiv x_{i}(mod\ \frac{p_{i}}{d})$。由此得到了$n$个方程的同余方程组(设每个同余方程的模数为$m_{i}$,即为上面的$\frac{p_{i}}{d}$),由于模数不一定互质,所以要用扩展CRT来求出最小的$x$。因为要保证攻击能将每条龙打到至少$0$血,即$x*t_{i}>=a_{i}$,所以要求出将每条龙打到$0$血或往下的最小次数的最大值$mx$,只要$x$小于$mx$就不停地给$x$加上$lcm(m_{1},m_{2}...m_{n})$。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<cstdio> #include<vector> #include<bitset> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define INF 4e18+10 using namespace std; ll c1,m1,c2,m2; int N,M,T; ll mx,x; ll lcm; multiset<ll>s; multiset<ll>::iterator it; ll a[100010]; ll p[100010]; ll b[100010]; ll t[100010]; ll c[100010]; ll m[100010]; ll quick(ll x,ll y,ll mod) { ll res=0ll; while(y) { if(y&1) { res=(res+x)%mod; } y>>=1; x=(x+x)%mod; } return res; } ll gcd(ll x,ll y) { return y==0?x:gcd(y,x%y); } void exgcd(ll a,ll b,ll &x,ll &y) { if(!b) { x=1;y=0; return ; } exgcd(b,a%b,y,x); y-=(a/b)*x; } ll inv(ll n,ll mod) { ll x,y; exgcd(n,mod,x,y); return (x%mod+mod)%mod; } int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&N,&M); s.clear(); mx=0; 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",&t[i]); } for(int i=1;i<=M;i++) { scanf("%lld",&x); s.insert(x); } for(int i=1;i<=N;i++) { it=s.upper_bound(a[i]); if(it!=s.begin()) { it--; } b[i]=*it; s.erase(it); s.insert(t[i]); } bool flag=false; for(int i=1;i<=N;i++) { mx=max(mx,(a[i]-1)/b[i]+1); ll d=gcd(b[i],p[i]); if(a[i]%d) { flag=true; break; } ll x,y; exgcd(b[i]/d,p[i]/d,x,y); ll P=p[i]/d; x=quick(x,a[i]/d,P); x=(x%P+P)%P; c[i]=x; m[i]=P; } if(flag) { printf("-1\n"); continue; } flag=false; m1=m[1],c1=c[1]; for(int i=2;i<=N;i++) { c2=c[i],m2=m[i]; ll d=gcd(m1,m2); if((c2-c1)%d) { flag=true; break; } ll g=inv(m1/d,m2/d); ll sum=quick((c2-c1)/d,g,m2/d); ll mod=quick(m1,m2/d,INF); sum=quick(sum,m1,mod); sum+=c1,sum%=mod; c1=sum,m1=mod; } if(flag) { printf("-1\n"); continue; } c1=(c1%m1+m1)%m1; ll ans=1ll; for(int i=1;i<=N;i++) { ll d=gcd(ans,m[i]); ans=quick(ans/d,m[i],INF); } if(c1>=mx) { printf("%lld\n",c1); continue; } ll res=ceil((double)(mx-c1)/ans); c1+=quick(ans,res,INF); printf("%lld\n",c1); } }