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))的更多相关文章

  1. LOJ.2721.[NOI2018]屠龙勇士(扩展CRT 扩展欧几里得)

    题目链接 LOJ 洛谷 rank前3无压力(话说rank1特判打表有意思么) \(x*atk[i] - y*p[i] = hp[i]\) 对于每条龙可以求一个满足条件的\(x_0\),然后得到其通解\ ...

  2. BZOJ5418[Noi2018]屠龙勇士——exgcd+扩展CRT+set

    题目链接: [Noi2018]屠龙勇士 题目大意:有$n$条龙和初始$m$个武器,每个武器有一个攻击力$t_{i}$,每条龙有一个初始血量$a_{i}$和一个回复值$p_{i}$(即只要血量为负数就一 ...

  3. BZOJ_5418_[Noi2018]屠龙勇士_exgcd+excrt

    BZOJ_5418_[Noi2018]屠龙勇士_exgcd+excrt Description www.lydsy.com/JudgeOnline/upload/noi2018day2.pdf 每次用 ...

  4. P4774 [NOI2018]屠龙勇士

    P4774 [NOI2018]屠龙勇士 先平衡树跑出打每条龙的atk t[] 然后每条龙有\(xt \equiv a[i](\text{mod }p[i])\) 就是\(xt+kp[i]=a[i]\) ...

  5. uoj396 [NOI2018]屠龙勇士

    [NOI2018]屠龙勇士 描述 小 D 最近在网上发现了一款小游戏.游戏的规则如下: 游戏的目标是按照编号 1∼n 顺序杀掉 n 条巨龙,每条巨龙拥有一个初始的生命值 ai .同时每条巨龙拥有恢复能 ...

  6. [洛谷P4774] [NOI2018]屠龙勇士

    洛谷题目链接:[NOI2018]屠龙勇士 因为markdown复制过来有点炸格式,所以看题目请戳上面. 题解: 因为杀死一条龙的条件是在攻击\(x\)次,龙恢复\(y\)次血量\((y\in N^{* ...

  7. LOJ 2721 「NOI2018」屠龙勇士——扩展中国剩余定理

    题目:https://loj.ac/problem/2721 1.注意别一输入 p[ i ] 就 a[ i ] %= p[ i ] ,因为在 multiset 里找的时候还需要真实值. 2.注意用 m ...

  8. 洛谷 P4774 [NOI2018] 屠龙勇士

    链接:P4774 前言: 交了18遍最后发现是多组数据没清空/ll 题意: 其实就是个扩中. 分析过程: 首先发现根据题目描述的选择剑的方式,每条龙对应的剑都是固定的,有查询前驱,后继(在该数不存在前 ...

  9. 【刷题】BZOJ 5418 [Noi2018]屠龙勇士

    www.lydsy.com/JudgeOnline/upload/noi2018day2.pdf Solution 将攻击的式子列出来,\(atk \times x-p \times y=a_i\) ...

随机推荐

  1. 解析ThreadPoolExecutor类是如何保证线程池正确运行的

    摘要:对于线程池的核心类ThreadPoolExecutor来说,有哪些重要的属性和内部类为线程池的正确运行提供重要的保障呢? 本文分享自华为云社区<[高并发]通过源码深度解析ThreadPoo ...

  2. rabbitMq接收实体

  3. 闭包 panic recover

    闭包=函数+外层变量的引用 recover必须搭配defer使用 defer一定要在可能引发panic的语句之前定义

  4. x和y为正整数变量,求满足 x+y | xy 的通解。

    x和y为正整数变量,求满足 x+y | xy 的通解. 解:由题设可知存在正整数t满足t(x+y)=xy. 设m=(x,y),则存在正整数u和v满足: x=mu, y=mv, (u,v)=1. 于是有 ...

  5. 一个double free相关问题的澄清

    引言 前一阵定位 Oracle 的 OCI 接口相关的一个内存释放问题,在网上看到了链接如下的这篇文章: 一个C++bug引入的许多知识 看到后面说 vector 里的两个单元里的内部成员指针地址是一 ...

  6. Java - 注释、标识符、关键字

    背景 要开始磕 Java 了,虽然以前学过用过,但是差不多忘光光了... 现在直接搬狂神的视频素材,不再自己总结,要学的东西太多了... 注释 单行注释 // 多行注释 /* */ 文档注释 /** ...

  7. 【Python从入门到精通】(二十五)Python多进程的使用

    您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦. 本篇重点介绍Python多进程的使用,读者朋友们可以将多进程和多线程两者做一个对比学习. 干货满满,建议收藏,需要用到时常看看. 小伙伴们如有问题 ...

  8. L2TP协议简介

    传送门:L2TP代码实现 1. L2TP 概述 L2TP(Layer 2 Tunneling Protocol,二层隧道协议)是 VPDN(Virtual Private Dial-up Networ ...

  9. C# List集合类常用操作:三、查找

    List集合查询数据 List<Employees> employees = new List<Employees>(); employees.Add(new Employee ...

  10. Spring-图解