bzoj 5418
这是拓展crt的典型应用
在你开始做之前,我一定要告诉你一件事情:虽然这道题看着和拓展crt模板很像,但他俩是有巨大的区别的!不要直接把板子改吧改吧扔上去!
题目模型:求解模线性方程组
其中p1,p2...pn不一定互质
第一眼:拓展crt板子题!
第二眼:等等...好像不太对
第三眼:WTF!系数哪来的!
我们知道,拓展crt的模板只能解决x系数为1的情况,而系数不为1的是很难做的!
什么?直接乘逆元变成1?
逆元不存在呢?
我们稍微做一点推导:
首先,我们解一下方程
设这个方程的一个解是x0(这是可以使用拓展gcd求解的)
那么这个方程的通解应该是,k∈Z
那么这个通解等价于方程的解
发现什么了吗?
是的!我们证明了方程与方程等价,这样就消掉了前面那个方程的系数!
所以,原方程组等价于这样:
这就很好了,我们使用正常的拓展crt解之即可
最后有几个细节问题:
①:对于ai>pi的情况,题目中给出的约束条件是pi=1,这样虽然拓展crt处理不了,但是我们可以应用特判过掉(p=1啊,多显然)
②:对于所有ai=pi的情况(即任一ai都=pi),只有当对应的攻击力是pi的倍数的时候才有解,否则无解,这个也要特判(有解也要特判,否则拓展crt解的结果会是0)
③:对于部分ai=pi的情况,如果对应攻击力不是pi的倍数则无解,但如果是pi的倍数,那么这个方程基本没用,可以替换成之类的形式
④:题目中运算很大,对于带取模的乘法需要快速加优化!同时所有数据类型建议使用long long以免挂掉
(求前驱那里本人使用的是treap,表示很好用)
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> #define ll long long #define ls tree[rt].lson #define rs tree[rt].rson #define INF 0x3f3f3f3f using namespace std; ll n,m; ll a[100005]; ll b[100005]; ll life[100005]; ll acc[100005]; ll p[100005]; ll s[100005]; int cyt=0; int rot=0; struct Treap { int lson; int rson; int huge; int same; ll val; int rank; }tree[200050]; void update(int rt) { tree[rt].huge=tree[ls].huge+tree[rs].huge+tree[rt].same; } void lturn(int &rt) { int temp=rs; rs=tree[rs].lson; tree[temp].lson=rt; tree[rt].huge=tree[temp].huge; update(temp); rt=temp; } void rturn(int &rt) { int temp=ls; ls=tree[ls].rson; tree[temp].rson=rt; tree[rt].huge=tree[temp].huge; update(temp); rt=temp; } void ins(int &rt,ll v) { if(!rt) { rt=++cyt; tree[rt].huge=1; tree[rt].same=1; tree[rt].val=v; tree[rt].rank=rand(); return; } if(v==tree[rt].val) { tree[rt].huge++; tree[rt].same++; return; }else if(tree[rt].val>v) { ins(ls,v); if(tree[ls].rank<tree[rt].rank) { rturn(rt); } }else { ins(rs,v); if(tree[rs].rank<tree[rt].rank) { lturn(rt); } } } void del(int &rt,ll v) { if(!rt) { return; } if(tree[rt].val==v) { if(tree[rt].same>1) { tree[rt].huge--; tree[rt].same--; return; }else if(ls*rs==0) { rt=ls+rs; return; }else { if(tree[ls].rank<tree[rs].rank) { rturn(rt); del(rt,v); }else { lturn(rt); del(rt,v); } } } tree[rt].huge--; if(tree[rt].val>v) { del(ls,v); }else { del(rs,v); } update(rt); } void query_pro(int rt,ll v,int typ) { if(!rt) { return; } if(tree[rt].val==v) { acc[typ]=v; return; }else if(tree[rt].val<v) { acc[typ]=tree[rt].val; query_pro(rs,v,typ); }else { query_pro(ls,v,typ); } } int query_min(int rt) { if(ls&&tree[ls].val!=-INF) { return query_min(ls); }else if(tree[rt].val!=-INF) { return tree[rt].val; }else { return query_min(rs); } } ll pow_add(ll x,ll y,ll mod) { ll ans=0; while(y) { if(y%2) { ans+=x; ans%=mod; } y/=2; x+=x; x%=mod; } return ans; } ll gcd(ll x,ll y) { if(y==0) { return x; } return gcd(y,x%y); } void ex_gcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x=1; y=0; return; } ex_gcd(b,a%b,x,y); ll t=x; x=y; y=t-(a/b)*x; } bool makeit() { for(int i=1;i<=n;i++) { if(acc[i]%p[i]==0) { if(p[i]==life[i]) { a[i]=1; b[i]=0; continue; }else { printf("-1\n"); return 1; } } ll x,y; ll r=gcd(acc[i],p[i]); if(life[i]%r) { printf("-1\n"); exit(0); } acc[i]/=r; ll temp=life[i]/r; ll tt=p[i]/r; ex_gcd(acc[i],tt,x,y); x=(pow_add(x,temp,tt)+tt)%tt; b[i]=x; a[i]=tt; } return 0; } ll ex_crt() { ll M0=a[1]; ll ans=b[1]; for(int i=2;i<=n;i++) { ll r=gcd(M0,a[i]); ll bb=((b[i]-ans)%a[i]+a[i])%a[i]; if(bb%r) { return -1; } bb/=r; ll M=M0/r; ll aa=a[i]/r; ll x,y; ex_gcd(M,aa,x,y); x=pow_add(x,bb,aa); ans+=x*M0; M0*=aa; ans=(ans%M0+M0)%M0; } return (ans%M0+M0)%M0; } ll T; int main() { scanf("%lld",&T); while(T--) { memset(tree,0,sizeof(tree)); rot=0; cyt=0; ins(rot,-INF); scanf("%lld%lld",&n,&m); for(int i=1;i<=n;i++) { scanf("%lld",&life[i]);//龙的生命力 } bool flag=0,flag1=0; for(int i=1;i<=n;i++) { scanf("%lld",&p[i]);//龙的恢复力 if(life[i]>p[i]) { flag=1; } if(life[i]!=p[i]) { flag1=1; } } for(int i=1;i<=n;i++) { scanf("%lld",&s[i]); } for(int i=1;i<=m;i++) { ll x; scanf("%lld",&x); ins(rot,x); } for(int i=1;i<=n;i++) { acc[i]=0; query_pro(rot,life[i],i); if(acc[i]==-INF) { acc[i]=query_min(rot); } del(rot,acc[i]); ins(rot,s[i]); } if(!flag1) { bool flag2=0; ll ans=1; for(int i=1;i<=n;i++) { if(life[i]%acc[i]!=0) { printf("-1\n"); flag2=1; }else { ll cd=gcd(ans,life[i]/acc[i]); ans*=life[i]/acc[i]/cd; } } if(!flag2) { printf("%lld\n",ans); } continue; } if(flag) { ll temp=0; for(int i=1;i<=n;i++) { if(life[i]%acc[i]!=0) { temp=max(temp,life[i]/acc[i]+1); }else { temp=max(temp,life[i]/acc[i]); } } printf("%lld\n",temp); continue; } if(makeit()) { continue; } printf("%lld\n",ex_crt()); } return 0; }