NOI2018屠龍勇士(擴展CRT + splay(multiset))
QWQ 一到假期就頹廢 哎
今年新鮮出爐的NOI題,QwQ同步賽的時候寫的,後來交了一發洛谷,竟然過了
首先 根據題目,我們很容易得到,假設對應每一條龍的劍的攻擊力是\(atk\)的話
\[a_i-x\times atk + k *p_i = 0 \]
\[x\times atk = a_i \pmod {p_i}\]
QwQ一看到這個式子,就想到了擴展crt求解。
不過我一開始的想法是,根據擴歐,求$a_i-x\times atk + k *p_i = 0 \(中的每一個\)x$的通解表達式,然後把它寫成同余的形式,最後再用擴展CRT合並
但是因為有點麻煩 所以沒寫
這裏提供\(was_n\)
就是通過求逆元,直接把\(atk\)弄到等式的右邊
只有當一個數和模數互質,他們才會有逆元
我們知道\[a_i-x\times atk + k *p_i = 0 \]
所以\[a_i= x\times atk + k *p_i\]
然後這個方程有解的條件是\(a_i | gcd(atk,p_i)\)
那麽我們先把等式兩邊同時除以\(gcd(atk,p_i)\) ,再寫成同余式子的形式\[x\times \frac{atk}{gcd} = \frac{a_i}{gcd} \pmod {\frac{p_i}{gcd}}\]
此時 $ \frac{atk}{gcd}\(和\)\frac{p_i}{gcd}$是互質的,所以一定存在逆元,然後就可以化成擴展crt的形式,之後求解就行
至於每一次確定\(atk\)的過程,只需要一顆平衡樹就行,不過要註意,可能會存在重復的元素,所以理論上不能用\(set\)
哦對!一個重要的事情!
在crt和一開始乘逆元的過程中,會爆long long,所以需要快速乘來進行乘法!這裏一定要註意!
以後遇到這種題,要想到快速乘!
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define ll long long using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while (!isdigit(ch)) {if (ch==‘-‘) f=-1;ch=getchar();} while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();} return x*f; } const int maxn = 3e5+1e2; ll lif[maxn],p[maxn],a[maxn],m[maxn]; ll atk[maxn]; ll sz[maxn],ch[maxn][4],fa[maxn]; ll cnt[maxn],size=2,n,mm,root,t; ll val[maxn]; ll jiangli[maxn]; bool flag=true; ll mul(ll i,ll j,ll p) { ll ans=0; while (j){ if (j&1) ans=(ans+i)%p; i=(i+i)%p; j>>=1; } return ans; } ll son (ll x) { if (x==ch[fa[x]][0]) return 0; else return 1; } void update(ll x) { sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x]; } void rotate(ll x) { ll y=fa[x],z=fa[y]; ll b=son(x),c=son(y); ch[z][c]=x; fa[x]=z; ch[y][b]=ch[x][!b]; fa[ch[x][!b]]=y; ch[x][!b]=y; fa[y]=x; update(y); update(x); } void splay(ll x,ll p) { while (fa[x]!=p) { ll y=fa[x],z=fa[y]; if (z==p) rotate(x); else if (son(x)==son(y)) { rotate(y); rotate(x); } else { rotate(x); rotate(x); } } if (fa[x]==0) root=x; } ll find_qq(ll x) { ll now = root,num=0; while (now) { if (val[now]<x) { num=now; now=ch[now][1]; } else now=ch[now][0]; } return num; } ll find_hj(ll x) { ll now = root,num=0; while (now) { if (val[now]>x) { num=now; now=ch[now][0]; } else now=ch[now][1]; } return num; } void insert(ll x) { ll qq=find_qq(x); ll hj=find_hj(x); splay(qq,0); splay(hj,qq); ll y=ch[hj][0]; if (cnt[y]) cnt[y]++,update(y); else { size++; fa[size]=hj; cnt[size]=1; val[size]=x; sz[size]=1; ch[hj][0]=size; splay(size,0); } } void delet(ll x) { ll qq=find_qq(x); ll hj=find_hj(x); splay(qq,0); splay(hj,qq); ll y=ch[hj][0]; if (cnt[y]>1) cnt[y]--,update(y); else { fa[y]=0; cnt[y]=0; val[y]=0; sz[y]=0; ch[hj][0]=0; } } ll gcd(ll a,ll b) { if (b==0) return a; else return gcd(b,a%b); } ll exgcd(ll &x,ll &y,ll a,ll b) { if (b==0) { x=1; y=0; return a; } ll cnt = exgcd(x,y,b,a%b); ll tmp = x; x=y; y=tmp-a/b*y; return cnt; } void init() { memset(ch,0,sizeof(ch)); memset(sz,0,sizeof(sz)); memset(fa,0,sizeof(fa)); memset(val,0,sizeof(val)); memset(a,0,sizeof(a)); memset(m,0,sizeof(m)); val[1]=1e18; val[2]=-1e18; fa[2]=1; ch[1][0]=2; root=1; size=2; flag=true; } ll niyuan(ll num,ll p) { ll ymh,szh; exgcd(ymh,szh,num,p); ymh=(ymh%p+p)%p; return ymh; } ll count(ll x) { ll hj=find_qq(x+1); if (val[hj]==-1e18){ ll ii = val[find_hj(val[hj])]; delet(ii); return ii; } else { ll ii = val[hj]; delet(ii); return ii; } } void solve1() { for (int i=1;i<=n;i++) { ll tmp = count(lif[i]); //cout<<tmp<<endl; ll gcd1=gcd(tmp,p[i]); // cout<<gcd1<<endl; m[i]=p[i]/gcd1; if (lif[i]%gcd1!=0) flag=false; if (!flag) return; lif[i]=lif[i]/gcd1; a[i]=mul(lif[i],niyuan(tmp/gcd1,m[i]),m[i]); insert(jiangli[i]); } } long long solve() { ll x0=0,M=0; x0=a[1]; M=m[1]; long long x=0,y=0; for (int i=2;i<=n;i++) { ll gcd1=exgcd(x,y,M,m[i]); if ((a[i]-x0)%gcd1!=0) flag=false; if (!flag) return -1; long long tmp = m[i]/gcd1; ll yyy=(a[i]-x0)/gcd1; yyy%=tmp; if (yyy<=0) yyy+=tmp; x=(x%tmp+tmp)%tmp; x=mul(x,yyy,tmp); ll ppp = M; M=M/gcd1*m[i]; ppp=(ppp%M+M)%M; x0=(mul(x,ppp,M)+x0%M)%M; } x0=(x0+M)%M; if (x0==0) x0+=M; return x0; } void solve3() { //cout<<"gg"<<endl; ll ans=-1e9; for (int i=1;i<=n;i++) { ll tmp = count(lif[i]); ll cnt = lif[i]/tmp; lif[i]=lif[i]%tmp; if (lif[i]>0) cnt++; ans=max(ans,cnt); insert(jiangli[i]); } cout<<ans<<endl; } int main() { //freopen("dragon.in","r",stdin); //freopen("dragon.out","w",stdout); cin>>t; while (t--) { init(); n=read(),mm=read(); int now = 0; for (int i=1;i<=n;i++) lif[i]=read(); for (int i=1;i<=n;i++) { p[i]=read(); if (p[i]==1) now++; } for (int i=1;i<=n;i++) jiangli[i]=read(); for (int i=1;i<=mm;i++) atk[i]=read(),insert(atk[i]); if (now==n) { solve3(); continue; } solve1(); if (!flag) { cout<<-1<<endl; continue; } //cout<<"hhh"<<endl; //for (int i=1;i<=n;i++) cout<<a[i]<<endl; ll ans=solve(); if (!flag) { cout<<-1<<endl; continue; } cout<<ans<<endl; } return 0; }
NOI2018屠龍勇士(擴展CRT + splay(multiset))